Geeks With Blogs

News

View Anthony Trudeau's profile on LinkedIn

Add to Technorati Favorites


Anthony Trudeau

Initialization parameters allow you to initialize the state of an event control based on your environment.  For example, you might have a connection string defined as a initialization parameter, so that the connection can easily be changed.  Registration arguments are conditions you place on the link between a producer and consumer.  The condition determines whether or not an appropriate event is passed to the consumer.

Initialization parameters and registration arguments are defined within the Event Workflow Editor and can be changed only at design-time.  This may be too restrictive.  What if you have a need to change the state on an object tied to the argument, and have that change persist?

Here's an example.  I'm creating scheduling event controls that will provide interval and calendar based functionality.  There is no issue with the interval schedules.  However, calendar based schedules shouldn't be able to run multiple times.  It could be problematic to have a daily scheduled event run twice on a day, because the Edge Process Manager (EPM) was restarted for some reason.

As you can see this example highlights the need to persist at least the last run date/time for the scheduled event.  But, there is no provision for this need in the iMotion Edgeware.  So what can be done?  Luckily, there is no restriction to the content of an initialization parameter or a registration argument.

In a previous article I discussed using objects (specifically object arrays) as initialization parameters or registration arguments.  To support this need the objects saved in the array must be serializable.  For the purposes of persistance you will also need a way to uniquely identify the object -- this could be a name or GUID.  But, otherwise the only limitation is your imagination.

Once you have your object all you need to do is implement some method of persistance to save the value.  In my case, using Isolated Storage was the best fit for the following reasons:

  1. The data I had to save was minimal (isolated storage has quotas)
  2. I didn't want the overhead of a database

I chose to isolate the persistance of my object in a separate class.  This class is marked as static and contains three methods:  Load, Save, and Delete.  Load is called when my schedule object is deserialized.  Save is called when the last run date/time is updated.  And Delete is called when a reset method is called.

Deletion requires special attention.  Deletion really only occurs from within the Event Workflow Editor in my case, because the schedule objects cannot be removed at run-time.  But, in the editors for the schedule I have to make sure that if the last run date/time has been called that I cleanup the file from Isolated Storage.  This could get a little kludgy.

Working with Isolated Storage is really easy.  Maybe the trickiest part is determining what store to use.  But, from my testing and some deduction you really only have one choice with the iMotion Edgeware -- assembly isolation by machine.  I'll walk you through the implementation of my ScheduleLastRunStorage class.  You can easily adapt this class to your use.

The first method I'll tackle is the Load method.  I'm using a GUID to identify my schedule objects, so that's all I need to pass in.  Internally I'm using the GUID as a file name, so I'll first create a helper method to return a nicely formatted string (i.e no special characters) for the file name.

        private static string GetFileName(Guid scheduleId)
        {
            return scheduleId.ToString("N");
        }

Nothing particularly interesting here.  The "N" format basically removes all the formatting characters from the GUID like the braces and hyphens.

To load from Isolated Storage you need to use two objects that are unique to Isolated Storage -- IsolatedStorageFile and IsolatedStorageFileStream.  The IsolatedStorageFile provides all the context necessary to get to the appropriate store.  The IsolatedStorageFileStream provides a stream object.  Accessing Isolated Storage requires appropriate permission, so I've marked up my method with declaritive security.

        [IsolatedStorageFilePermission(SecurityAction.Demand, 
                UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByMachine)]
        public static DateTime Load(Guid scheduleId)
        {
            IsolatedStorageFile isoFile = null;
            IsolatedStorageFileStream isoStream = null;
            StreamReader reader = null;
            DateTime lastRun = DateTime.MinValue;
            string fileName = GetFileName(scheduleId);

            try
            {
                isoFile = IsolatedStorageFile.GetMachineStoreForAssembly();
                isoStream = new IsolatedStorageFileStream(fileName, FileMode.Open, FileAccess.Read, isoFile);
                reader = new StreamReader(isoStream);

                DateTime.TryParse(reader.ReadLine(), out lastRun);
            }
            catch (FileNotFoundException)
            {
                Debug.WriteLine("Isolated storage file was not found: " + fileName);
            }
            finally
            {
                if (reader != null)
                    reader.Dispose();

                if (isoStream != null)
                    isoStream.Dispose();

                if (isoFile != null)
                    isoFile.Dispose();
            }

            return lastRun;
        }

I've trapped the FileNotFoundException, because it could happen, but shouldn't in my implementation.  I'm using the System.Diagnostics.Debug class to pump a message out in case there are problems.

The next method is Save.  All I care about persisting is the last run date/time, so there's no reason to pass in the entire object.  Other than the arguments the implementation is quite similar to the Load method including the declarative security.

        [IsolatedStorageFilePermission(SecurityAction.Demand, 
                UsageAllowed=IsolatedStorageContainment.AssemblyIsolationByMachine)]
        public static void Save(Guid scheduleId, DateTime lastRun)
        {
            IsolatedStorageFile isoFile = null;
            IsolatedStorageFileStream isoStream = null;
            StreamWriter writer = null;
            string fileName = GetFileName(scheduleId);

            try
            {
                isoFile = IsolatedStorageFile.GetMachineStoreForAssembly();
                isoStream = new IsolatedStorageFileStream(fileName, FileMode.Create, FileAccess.Write, isoFile);
                writer = new StreamWriter(isoStream);

                writer.WriteLine(lastRun);
                writer.Flush();
            }
            finally
            {
                if (writer != null)
                    writer.Dispose();

                if (isoStream != null)
                    isoStream.Dispose();

                if (isoFile != null)
                    isoFile.Dispose();
            }
        }

The last method that we need to implement is the Delete method.  The IsolatedStorageFile class provides a DeleteFile method that makes this the simplest of the three methods to implement.

        [IsolatedStorageFilePermission(SecurityAction.Demand,
                UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByMachine)]
        public static void Delete(Guid scheduleId)
        {
            using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetMachineStoreForAssembly())
            {
                string fileName = GetFileName(scheduleId);
                isoFile.DeleteFile(fileName);
            }
        }

The use of Isolated Storage provides the best of both worlds for me.  For one, I am able to persist my schedule objects, so that daily schedules really only run on a daily basis; and two, I don't have any overhead that a database would bring to the table.

Posted on Tuesday, May 2, 2006 4:00 PM | Back to top


Comments on this post: Changing initialization parameters and registration arguments at run-time

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Anthony Trudeau | Powered by: GeeksWithBlogs.net