in

ASP.NET Weblogs

This Blog

Syndication

ShowUsYour<Blog>

Irregular expressions regularly

Using metadata and reflection to dynamically manage message routing

 

Most routing systems have a transformation phase where, based on its current state, a message is transformed into a document and routed to an endpoint.  Systems such as BizTalk provide GUI's and designers to remove the need for cumbersome coding by making the rules and subsequent transformations configurable; here's an example of a switch statement in a listener class where the rules of the routing engine are hard-coded:

 

public static void MessageArrived( Message message ) {
  switch( message.MessageState ) {
    case MessageState.Initial:
        Console.Write( Transformer.CreateRequest( message ) );
        break ;
    case MessageState.Submitted:
        Console.Write( Transformer.CreateApproval( message ) );
        break ;
    case MessageState.Approved:
        Console.Write( Transformer.CreateInvoice( message ) );
        break ;
    case MessageState.Saved:
        Console.Write( Transformer.CreateReport( message ) );
        break ;
    default:
        Console.Write( Notifier.NotifyError( message ) );
    break ;

  }
}

 

If there's extra "noise" in the MessageArrived method, it can become hard to maintain as the length of the switch gets longer.  It can also become hard to maintain if there are repetitive code chunks within each case.

 

In the above cases you can - at a moderate performance cost - re-factor the common code away into a generic method.  Looking at the above example, one neat way to achieve this is to ascribe metadata to the MessageState enum so that it can be inspected at runtime and the routing lookup driven from that metadata.  First, let's create an attribute to contain our lookup data and add it to the MessageState enum:

 

[AttributeUsage(AttributeTargets.Field, Inherited=false, AllowMultiple=true)]
public class WorkflowAttribute : Attribute {
  public WorkflowAttribute(Type type, string methodName) {
    this.Type = type ;
    this.MethodName = methodName ;
  }

  public Type Type;
  public string MethodName ;
}


public enum MessageState : short {

  [WorkflowAttribute(typeof(Transformer), "CreateRequest")]
  Initial = 1,

  [WorkflowAttribute(typeof(Transformer), "CreateApproval")]
  Submitted = 2,

  [WorkflowAttribute(typeof(Transformer), "CreateInvoice"),

   WorkflowAttribute(typeof(Notifier), "NotifySalesGuy")]
  Approved = 3,

  [WorkflowAttribute(typeof(Transformer), "CreateReport")]
  Saved = 4,

  [WorkflowAttribute(typeof(Notifier), "NotifyError")]
  Unknown = short.MaxValue
}

 

Notice that I applied 2 worklow attributes to the MessageState.Submitted enum value.

 

Now I can re-factor the original MessageArrived method into a generic message handler routine:

 

public static void MessageArrived( Message message ) {

  MessageState state = message.MessageState ;
  FieldInfo field = state.GetType().GetField(state.ToString()) ;

  object[] attribs = field.GetCustomAttributes(typeof(WorkflowAttribute), false) ;

  for( int i=0; i<attribs.Length; i++ ) {
    WorkflowAttribute att = attribs[i] as WorkflowAttribute ;
    if( att != null ) {
      MethodInfo method = null ;

      if( att.Type.GetMethod(att.MethodName).IsStatic ) {
        method = att.Type.GetMethod(att.MethodName) ;
        Console.Write(method.Invoke(null, new object[] {message}));
      }else{
        object instance = Activator.CreateInstance(att.Type) ;
        method = instance.GetType().GetMethod(att.MethodName);
        Console.Write(method.Invoke(instance, new object[] {message}));
      }
    }
  }
}

 

 

I've uploaded a working demo of this to ProjectDistributor:

 

    http://projectdistributor.net/Releases/Release.aspx?releaseId=82

Published Jan 20 2005, 06:40 PM by digory
Filed under: ,

Comments

 

TrackBack said:

She loves me, she loves me not.
January 19, 2005 1:29 PM
 

TrackBack said:

January 19, 2005 2:33 PM
 

Geoff Appleby said:

Hey man, your post reminded me of something I've been meaning to write about for a while. Hope you don't mind :)
January 20, 2005 4:29 AM
 

Howard van Rooijen said:

I posted about enums, attributes & reflection last year -

http://www.1succeeded0failed0skipped.com/PermaLink.aspx?guid=17407214-7174-4454-9c6e-48688b672ab7

it covers how you could use them for decorating asp.net pages or for use in a data access layer.

There is also a nice lists of links at the bottom of the article.
January 20, 2005 7:38 AM
 

Darren Neimke said:

Geoff... thanks for your post; I'm glad that you brought those raised those important points. I actually meant to mention that stuff but kinda forgot. When I wrote:

> In the above cases you can - at a
> moderate performance cost - re-factor the common
> code away into a generic method.

I was going to add that probably wouldn't want to do something like this in code which runs in a tight loop because it can be anywhere up to 100x slower than the straight switch statement example.

Again, thanks for picking me up on that.


January 20, 2005 2:31 PM
 

Daren Neimke said:

Daren,

There is a much more efficient (and elegant) solution to this if you plan to use Whidbey. Altough the reflection solution is very as well.

You could use the new Lightweight Code Generation feature to "dynamically" create the switch statement at runtime. Once the dynamec method is created, you could call it without performance hit (assuming that you are as good as the compiler to generate the switch )

DynamicMethod is the class name that implements LCG.
January 22, 2005 12:20 AM
 

TrackBack said:

January 22, 2005 12:48 PM

Leave a Comment

(required)  
(optional)
(required)  
Add