posts - 114, comments - 204, trackbacks - 82

My Links

News

View Anthony Trudeau's profile on LinkedIn

Add to Technorati Favorites

Article Categories

Archives

Post Categories

Image Galleries

Other Links

Object arrays as initialization parameters and registration arguments

The iMotion Edgeware framework provides initialization parameters for event controls as well as registration arguments for the link between a producer and consumer.  The initialization parameters and registration arguments only support string values, because they are saved as XML elements within the project configuration file.

Sometimes it is necessary to save an object that may have many fields as either an initialization parameter or a registration argument.  Or worse if you want an array of objects.  In my case, I wanted the ability to assign an array of my objects that would serve as registration arguments.  Fundamentally, you have three options if you want to persist an object or an array of objects:

  • Write each field of the object to a separate name-value pair
  • Create a ToString and Parse formatting implementation
  • Serialize the object graph

The first two options are too much work and too much code unless your class has very few fields.  And the more code the greater chance for bugs. So, serialization is really the only viable choice.  Unfortunately, serialization comes with its own headaches.

There are also three built-in options for serialization:

  • Binary serialization
  • XML serialization
  • SOAP serialization

I was pretty sure that binary serialization wouldn't work, because there are some characters within the output that would not be encoded correctly into a string.  I didn't explore all possible options with encoding, so there is a possibly that some encoding like ANSI would work, but that could bring its own problems depending on the object being serialized.

XML serialization sounded promising, because its easy and requires fewer permissions than standard serialization using a BinaryFormatter or a SoapFormatter.  And more importantly it doesn't store version information on the serialized type.  However, XML serialization comes with some limitations including:

  • All properties to be serialized must have get and set accessors
  • Certain types cannot be serialized (e.g. TimeSpan -- this blows my mind, since the XML duration type is the same thing)
  • Markup is required if your object contains other types
  • Supporting an object hierarchy requires complex setup of the XmlSerializer

I wrestled with using the XmlSerializer for awhile before realizing that it just wasn't flexible enough.  I actually had it working except for my TimeSpan objects.  But, it came with the cost of a very messy marked up class.

I had never used the SoapFormatter before, because in most cases I could use either an XmlSerializer or a BinaryFormatter.  After some initial testing it was clear that the SoapFormatter didn't come with any of the drawbacks of the other options.  The one requirement in my case was implementing ISerializable on my types, because I had some read-only fields.

The issue with versioning was the first thing I had to tackle.  I discovered that both formatters have the AssemblyFormat property which has two options:  Simple and Full.  Simple outputs just the name of the assembly; whereas Full specifies the fully qualified name including the version and public key.  It's critical that the serialized data not be version dependent with initialization parameters and registration arguments; otherwise, all of your data will be invalid when the version of the components is changed.

Once the choice of serialization was settled the implementation flew together very easily.  The first thing I did was isolate the implementation of the serialization and deserialization into a special class.  The class doesn't require member fields; therefore, I made the class static.  I implemented the class using two public methods with a few helper methods that perform some of the core work.

Typically, serialization occurs to a file.  In my case I want to serialize to a string.  This is no problem thanks to the MemoryStream class and the Encoding classes.  Here are the steps for the serialization:

  • Create a MemoryStream
  • Create the SoapFormatter
  • Serialize the object graph to memory
  • Encode the bytes from memory into a string

The process for deserialization is for all intents and purposes the exact opposite.  Here are the steps for deserialization:

  • Decode the string into bytes
  • Create a MemoryStream from the bytes
  • Create the SoapFormatter
  • Deserialize the object graph

Here is the completed code for the serializer class:

public static class ScheduleSerializer
{
     public static Schedule[] Deserialize(string data)
     {
          if (data == null || data.Length == 0)
               throw new ArgumentNullException("data");

          using (MemoryStream stream = GetStreamFromString(data))
          {
               SoapFormatter formatter = CreateFormatter();
               Schedule[] schedules = (Schedule[])formatter.Deserialize(stream);

               return schedules;
          }
     }

public static string Serialize(Schedule[] schedules)
{
     if (schedules == null || schedules.Length == 0)
          throw new ArgumentNullException("schedules");

     using (MemoryStream stream = new MemoryStream())
     {
          SoapFormatter formatter = CreateFormatter();
          formatter.Serialize(stream, schedules);

          return GetStringFromStream(stream);
     }
}

private static SoapFormatter CreateFormatter()
{
     SoapFormatter formatter = new SoapFormatter();
     formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;

     return formatter;
}

private static MemoryStream GetStreamFromString(string data)
{
     byte[] dataBytes = Encoding.UTF8.GetBytes(data);
     MemoryStream stream = new MemoryStream(dataBytes);

     return stream;
}

private static string GetStringFromStream(MemoryStream stream)
{
      byte[] dataBytes = stream.ToArray();
      string data = Encoding.UTF8.GetString(dataBytes);

     return data;
}

This class is a great utility when it comes to using complex initialization parameters and registration arguments in the iMotion Edgeware platform.  Using the utility is extremely easy.  For initialization parameters you would call Deserialize during the OnLoad method and then Serialize in the SaveProperties method.  And then Deserialize again in the Init method for the event control.  For registration arguments you would Deserialize in the OnLoad method, Serialize before exiting your EventProducerLinkDialog, and then Deserialize again in the AddEventConsumer method within the event producer.

Here's the relevant code from within my EventProducerLinkDialog:

protected override void OnLoad
{
     string serializedData = this.RegistrationArgs[SCHEDULEDATA_ARG];

     if (serializedData != null && serializedData.Length > 0)
    { 
         Schedule[] schedules = ScheduleSerializer.Deserialize(serializedData);

          foreach (Schedule schedule in schedules)
         {
              AddScheduleListItem(schedule);     // code for this method not shown
         }
     }
}

private void okayButton_Click(object sender, EventArgs e)
{
     if (HasChanged) 
     { 
           Schedule[] schedules = GetSchedulesFromItems();    // code for this method not shown
           string serializedSchedules = ScheduleSerializer.Serialize(schedules); 
             
           RegistrationArgs[SCHEDULEDATA_ARG] = serializedSchedules; 
           RegistrationArgsChanged = true;
      }

      DialogResult = DialogResult.OK;
      Close();
}

Here's the relevant code from the event producer:

public void AddEventConsumer(IEventConsumer eventConsumer, NameValueCollection args)
{
     IScheduledEventConsumer newConsumer = eventConsumer as IScheduledEventConsumer; 

     if (newConsumer != null)
    {
          string serializedData = args[SCHEDULEDATA_ARG];
          Schedule[] schedules = ScheduleSerializer.Deserialize(serializedData);
          List enabledSchedules = new List(schedules.Length);

           // filter out disabled schedules
           foreach (Schedule schedule in schedules)
          {
               if (schedule.Enabled)  
                  enabledSchedules.Add(schedule);
           }

          // add the consumer and it's schedules 
         _consumers.Add(newConsumer, enabledSchedules.ToArray()); 
     }
}

This article demonstrated how to use complex objects and object arrays for more advanced use in initialization parameters and registration arguments for your iMotion Edgeware event controls.  You'll quickly see how much more customization you can offer to the workflow users using these techniques.

  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Print | posted on Saturday, March 11, 2006 5:00 PM |

Feedback

Gravatar

# Object arrays as initialization parameters and registration arguments

The iMotion Edgeware platform provides two methods for configuration of the event controls namely initialization parameters and registration arguments.

Initialization parameters are tied to an individual event control based assigned either as attributes in the default editor or through a custom editor when the event control implements the IEventControlDialogProvider interface.

Registration arguments provide a method for an event producer to filter events to a particular consumer by assigning properties to the link between the producer and consumer. These properties are called registration arguments and they are assigned through attributes for the producer, or through a custom dialog when the producer implements the IEventProducerLinkDialogProvider interface.

Both initialization parameters and registration arguments are name-value string pairs that are saved in the XML-based workflow project. Sometimes a simple string is insufficient. But, using serialization it is possible to have objects or arrays of objects as your initialization parameters and registration arguments.
3/11/2006 5:09 PM | Anthony Trudeau
Gravatar

# Object arrays as initialization parameters and registration arguments

The iMotion Edgeware platform provides two methods for configuration of the event controls namely initialization parameters and registration arguments.

Initialization parameters are tied to an individual event control assigned either as attributes in the default editor or through a custom editor when the event control implements the IEventControlDialogProvider interface.

Registration arguments provide a method for an event producer to filter events to a particular consumer by assig
3/11/2006 5:14 PM | Anthony Trudeau
Gravatar

# Changing initialization parameters and registration arguments at run-time

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?
5/2/2006 4:00 PM | Anthony Trudeau
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
 
 

Powered by: