Click here to Skip to main content
Click here to Skip to main content

Test COM+ Event Without Writing a Subscriber.

, 14 Aug 2002
Rate this:
Please Sign up or sign in to vote.
The COM+ Event Subscriber Studio (ESS) is a lite tool to test a COM+ Event System Notification without writing a Subscriber. This article describes a loosely coupled design and implementation details of the 'Virtual Subscriber' and its usage in the ESS Tool.

Contents

Introduction

The COM+ Event System Notification is based on the loosely coupled Publisher/Subscriber design pattern using the Event Class metadata (contract) to tight them together during the run time. Using the LCE (loosely coupled events) in the application architecture required to design and implement in prior only the Event Class and Publisher. The Subscribers are usually part of the incremental development phases. Publishing a business logic and its control workflow allows to enhance an application for features such as post-processing, simulation, debugging, tuning, and monitoring business logic.

Tracing and debugging a COM+ LCE notification (part of the Enterprise services) makes easy using the COM+ Event Subscriber Studio (ESS) Tool described in this article. The ESS Tool is a Virtual Subscriber for any Event Class to examine the fired event calls included their run-time values. This feature is very helpful during the development process and unit testing.

Creating the transient subscription request will dynamically generate a subscriber in memory, link to the user interface and its registration in the COM+ Event System Store. The event calls received by the virtual subscriber are stored into the TreeView layout included their arguments name, type and value such as soap and xml formatted text, dataset, tables and rows. Exploring this TreeView will allow to see a published data by the LCE Publisher source.

In this article I will show you all "magic" of the virtualization using the .Net Framework namespaces such as Reflection and CodeDom. Also you will find a programming technique and usage of the TreeView, ToolTip and delegates.

Concept and Design

The concept of the Virtual Subscriber is based on generating and compiling its source code on the fly. The source code template of the virtual subscriber is populated in the both sides such as Event System and Consumer in the manner of the delegating the event calls to the destination handler (in our case it's the Windows Form).

The following picture shows a position of the Virtual Subscriber in the COM+ LCE System Notification:

To receive the event call from the Event System, the suitable subscription has to be subscribed to the Event System. The ESS tool is based on the transient subscriptions only. It's fine for our test purposes such as inspecting the event call contents. Creating the subscription is straightforward work if you know all required properties:

  • Name - human-readable text describes the name of the subscription (option)
  • Description - text describes more details about the subscription (option)
  • ID - unique identifier for this subscription (required)
  • Enabled - boolean delivery flag to enable/disable this subscription (default is true)
  • EventClassID - the Event Class identifier either the ProgId or clsid (required)
  • MethodName - name of the event's method from which subscriber wants to receive incoming event calls (required)
  • FilterCriteria - string to filtering events at the Event Class (option, default value is null)
  • SubscriberInterface - actually reference of the subscriber object, which wants to receive the events, in our case it's the virtual subscriber (required)
  • PublisherProperty - the property bag for publisher filter (option, default value is null)
  • SubscriberCLSID - null for this transient subscription
  • PublisherID - null, we want to receive a specified events from any publisher
  • InterfaceID - null for this transient subscription
  • SubscriberMoniker - null for this transient subscription

The above bolded properties are deceived to create a properly subscription. Based on the user request such as EventClassID and MethodName the tool has enough information to create subscription and register it into the Event System Store. The ID is generated by the Guid class, it's easy, but the SubscriberInterface property required the reference to the existed class - subscriber. The task is simple for the one specific event class and method, which it can be hard coded in the implementation file. In the case of the unknown event class and method, we need to design the virtual subscriber and generate its assembly on the run time.

The following code snippet shows the template of the Virtual Subscriber:

private string strSrcCodeTemplate = 
   "namespace _NAMESPACE { " +            // namespace
   "   using System;" +
   "   using System.Reflection;" + 
   "   using System.Xml;" +
   "   using System.Data;" +              // for Dataset, ...
   "   _USING" +                          // more references on the fly
   "   public class _CLASSNAME { " +      // subscriber class
   "      string m_id=null;" +            // subscriber ID
   "      MulticastDelegate m_md=null;" + // handler delegator
   "      public _CLASSNAME(string id, object handler) {" + // ctor
   "         m_id=id;" +
   "         m_md=handler as MulticastDelegate;" +
   "      }" +
   "      public void _METHODSIGNATURE {" +           // event method
   "         object[] args=new object[]{_ARGUMETS};" +  
                      // serialize method's arguments
   "         if(m_md!=null)" + // if handler exists
   "            m_md.Method.Invoke(m_md.Target, 
            new object[]{m_id, args});" + //pass arguments to handle
   "      }" +
   "    }" +
   "}";

As you can see, it's a very lightweight class. The bolded text represents its virtualization. These places are replaced by the actually values on the fly. The incoming event calls will perform the following:

  • serializing name and value of the method's arguments to the object array
  • invoking the delegate method

These couple lines in the event method makes powerful mapping of the unknown event method to the generic known target (consumer) handler. I used a "magic" delegate/event design pattern to forward the event calls to the consumer side, however I don't using the event class to store a delegate object. I just casting a delegate object to the MulticastDelegate type and simple invoking a method on the target object. This trick makes a loosely coupled design for the consumer side usage.

After compiling the populated subscriber template source code and creating its instance using the Activator class, we obtained the reference to the subscriber object requested by the transient subscription (SubscriberInterface).

Registering the subscription in the Event System Store we are the part of the Event System Model and ready to receive particularly event calls. This is a first part of the COM+ Event Subscriber Studio - Subscription Administrator.

The second part of the ESS Tool is dedicated to incoming event calls. Its design is straightforward and user interface oriented, based on the TreeView controller. The event calls are received by the handler delegated to the virtual subscriber using the following delegator signature:

public delegate void NotifyHandler(string id, object[] p);

where:

  • id specifies the subscription identifier generated by the ESS Subscription Administrator
  • p specifies array of the event call arguments - name and value.

Based on the above parameters, the TreeView controller is populated using the icon and label properties. Note that the virtual subscriber runs on the different thread than the TreeView controller, that's way the marshalling is necessary to perform it using the Invoke design pattern.

Implementation

The ESS Tool has been implemented by the following two assemblies:

  • VirtualSubscriber.dll - This is a reusable class to generate a subscriber during the run-time. It can be used also to create a specific subscriber in .Net Application.
  • SubscriptionStudio.exe - This is a Windows Form program to handle administration of the subscription and displaying the fired event calls.

VirtualSubscriber

Implementation of the Virtual Subscriber is based on the single class - SubscriberFactory, which has only one public method to create an instance of the subscriber with specified arguments. The object reference of the subscriber is returned back to the caller.

The following code snippet show that:

 
public object CreateInstance(string id, MethodInfo miEvent, object handler, 
                             string[] refUsing, string[] refAsm) 
{
   //current namespace
   string thisNamespace = this.GetType().Namespace;

   //name of the subscriber class
   string className = "Subscriber" + "_" + this.GetHashCode().ToString();

   // Create type of the Virtual Susbriber (VS)
   string source = CreateSourceCode(thisNamespace, refUsing, 
           className, miEvent);
   Assembly asm = CompileFromSource(source, refAsm);
   Type tVS = asm.GetType(thisNamespace + "." + className);

   // Create instance of the VS
   return Activator.CreateInstance(tVS, new object[]{id, handler});
}

public object CreateInstance(string id, MethodInfo miEvent, object handler) 
{
   return CreateInstance(id, miEvent, handler, null, null);
}

Its logic is very simple using the helper methods such as CreateSouceCode and CompileFromSource.

The first one will populate a hard-coded subscriber template by replacing its special words (_NAMESPACE, _USING, _CLASSNAME, _METHODSIGNATURE, _ARGUMETS) with actually strings. The Reflection namespace classes are using to obtain a type and name of the event method's arguments necessary to accomplish this task .

private string CreateSourceCode(string strNs, string[] references, 
  string strClassName, MethodInfo mi) 
{
   StringBuilder sbSrc = new StringBuilder(strSrcCodeTemplate);
   sbSrc.Replace("_NAMESPACE", strNs);

   //using
   if(references != null) 
   {
      StringBuilder sbUsing = new StringBuilder();
      foreach(string s in references)
      {
         if(s != null && s.Length > 0)
            sbUsing.AppendFormat("using {0};", s);
      }
      sbSrc.Replace("_USING", sbUsing.ToString());
   }
   else 
   {
      sbSrc.Replace("_USING", "");
   }

   sbSrc.Replace("_CLASSNAME", strClassName);

   //method signature and arguments
   StringBuilder sbMS = new StringBuilder();
   StringBuilder sbArgs = new StringBuilder();
   sbMS.AppendFormat("{0}(", mi.Name);
   foreach(ParameterInfo pi in mi.GetParameters()) // method's arguments
   {
      sbMS.AppendFormat("{0} {1} ,", pi.ParameterType.Name, pi.Name);
      sbArgs.AppendFormat("\"{0}\", {1},", pi.Name, pi.Name);
   }
   sbMS.Remove(sbMS.Length - 1, 1);     // remove last comma
   sbMS.Append(")");
   sbArgs.Remove(sbArgs.Length - 1, 1); // remove last comma

   //
   sbSrc.Replace("_METHODSIGNATURE", sbMS.ToString());
   sbSrc.Replace("_ARGUMETS", sbArgs.ToString());

   return sbSrc.ToString();
}

When we have an actually source code, we can call the following helper function to obtain its assembly:

 
private Assembly CompileFromSource(string source, string[] refasm)
{
   // assembly compilation.
   CompilerParameters cp = new CompilerParameters();
   cp.ReferencedAssemblies.Add("System.dll");
   cp.ReferencedAssemblies.Add("System.Data.dll");
   cp.ReferencedAssemblies.Add("System.Xml.dll");
   if(refasm != null)
      cp.ReferencedAssemblies.AddRange(refasm);
   cp.GenerateExecutable = false;
   cp.GenerateInMemory = true; 
   cp.IncludeDebugInformation = false; 
   ICodeCompiler icc = new CSharpCodeProvider().CreateCompiler();
   CompilerResults cr = icc.CompileAssemblyFromSource(cp, source);
   if(cr.Errors.Count > 0)
   {
      StringBuilder sb = new StringBuilder();
      foreach(CompilerError ce in cr.Errors) 
      {
         sb.Append(ce.ErrorText);
      }
      throw new Exception(string.Format("Build failed: {0}", 
             sb.ToString())); 
   }
   // return type 
   return cr.CompiledAssembly;
}

As you can see, compiling the source code on the fly is straightforward using the .Net Framework namespaces such as CodeDom, CodeDom.Compiler and Microsoft.CSharp. You can also add additional assemblies via the argument refasm. In the case of compiling error(s), the exception is thrown with passing more error details to its caller.

WindowsForm

This GUI of the ESS Tool is divided into two panels and both of them are driven by TreeView Controllers. The left panel is dedicated to administrate subscriptions and the right one is for displaying received event calls. I am going to focus on the part related to the Virtual Subscription, only. Clicking on the node which allows to register subscription, the following handler function is performed:

private void Click_Register(object sender, System.EventArgs e)
{
   try 
   {
      // ... get the values from the subscription nodes 

      //create a transient subscriber on the fly
      string id = subscriptionName + "[" + subscriptionId + "]." 
            + methodName;
      MethodInfo mi = typeofEventClass.GetMethod(methodName);
      SubscriberFactory sf = new SubscriberFactory(); 
      subscriber = sf.CreateInstance(id, mi, this.m_EventHandler, 
           references, dlls);

      // create LCE transient subscription for VS
      IEventSystem es = new EventSystem.EventSystem() as IEventSystem;
      IEventSubcription2 sub = new EventSubcription() as IEventSubcription2;
      sub.SubscriptionName = subscriptionName;
      sub.Description = subscriptionDesc;
      sub.SubscriptionID = "{" + subscriptionId + "}";
      sub.Enabled = true;
      sub.EventClassID = "{" + typeofEventClass.GUID.ToString() + "}";
      sub.MethodName = methodName;
      sub.FilterCriteria = filterCriteria; 
      sub.SubscriberInterface = subscriber;
      es.Store("EventSystem.EventSubscription", sub); 
            // registry into the Event System Store

      //state
      tn.ImageIndex = 1; //Registered/Enable
      tn.SelectedImageIndex = tn.ImageIndex; 
      UpdateSubscriptionMenu(tn);
   }
   catch(Exception ex) 
   {
      MessageBox.Show(ex.Message, m_thisProcess.ProcessName,
               MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
}

The above code snippet shows an interaction with the virtual subscriber to obtain its object reference and creating the subscription object and its registration in the COM+ event System.

The right panel GUI is a background driven task. Each time the event call is received, its handler adding a new node to its TreeView root. This child node has own node layout depends from the type and numbers of the event's arguments.

The following code snippets show creating the handler delegator and its callback method - OnNotifyHandler :

Declaration

private delegate int AddToTreeView(TreeNode node);    
private delegate void NotifyHandler(string id, object[] p);
private object m_EventHandler = null;

Initializing

public Form1() 
{
   // ...
   
   //handler to subscriber 
   m_EventHandler = new NotifyHandler(OnNotifyHandler);
}

Handling

private void OnNotifyHandler(string id, object[] p) 
{
   TreeNode tnRoot = treeViewEvent.Nodes[0];

   try 
   {
      TreeNode tnParent = new TreeNode(id, 0, 1);

      for(int ii = 0; ii < p.Length; ii++) 
      {
         string name = p[ii++].ToString();
         object obj = p[ii];
         TreeNode tnArg = new TreeNode(name, 2, 2);

         if(obj is DataSet)
         {
            AddDataset(tnArg, obj);
         }
         else
         if(obj is string) 
         {
            AddText(tnArg, obj);
         }
         else
         {
            AddObject(tnArg, obj); 
         }

         tnParent.Nodes.Add(tnArg);
      }

      if(treeViewEvent.InvokeRequired == true) 
                // check if we running within the same thread
         treeViewEvent.Invoke(new AddToTreeView(tnRoot.Nodes.Add), 
                  new object[] {tnParent});
      else
      tnRoot.Nodes.Add(tnParent); 
   }
   catch(Exception ex) 
   {
      Trace.WriteLine(ex.Message);
   }
}

Usage

Using the COM+ Event Subscription Studio is not complicated task. There is one root - Transient Subscriptions to hold a collection of the subscription nodes. Each node included the root one has own state and pop menu to select a particular action. For instance, the root node offering to add a new subscription or delete all of them. The icon of the subscription node indicated its state. The following picture shows these states:

When subscription is unregistered or new, its properties can be modified using the formula entry name = value [, ] where comma is delimiter between values (needed for Ref and Dll only).

After registering subscription, the ESS Tool is ready to receive and display incoming event calls on the right panel under the Fired Events root. Expanding the nodes, we can inspected the particular event call arguments for their types and values.

The following picture shows some types and values of the event call:

Exiting the ESS Tool the following process is performed:

  • unregistering all registered subscriptions from the COM+ Event System.
  • storing the collection of the subscription nodes in the SubscriptionStudio.xml file, which can be loaded back during the initializing process.

Test

I created a solution to test the ESS Tool. There is a set of the following projects:

  • SubscriptionStudio - Windows Form, program
  • VirtualSubscriber - subscriber on the fly, library
  • EventClassSample- event class, library
  • PublisherSample - publisher to fire an event, program

Note that the last 2 projects have been designed and implemented only for test purposes.

Follow these steps to make your first test:

  • Step 1. Launch the PublisherSample program. If this is a first time, then click the button FireEvent to perform a lazy registration of the Event Class in the COM+ Catalogue
  • Step 2. Launch the SubscriptionStudio program.
  • Step 3. Expand the main root.
  • Step 4. Right click on the Subscription node and select Register. You will see the node icon has been changed.
  • Step 5. Fire Event from the Publisher
  • Step 5. Exanimate the received event call in the right panel of the ESS Tool.

Conclusion

The COM+ Event Subscription Studio is a useful tool for developers, whose are building the COM+ Event Publisher/Subscriber system notifications. This article described its "heart" - Virtual Subscriber and programming technique of the loosely coupled design pattern. The GUI part is a lite version. The best solution is incorporated windows controllers into the MMC environment and extended for virtualization also the Event Classes and Publishers included their test of the firing events.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Roman Kiss
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
GeneralUsing persistent subscriptions/eventclasses with windows forms PinmemberIlse16-May-06 4:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141220.1 | Last Updated 15 Aug 2002
Article Copyright 2002 by Roman Kiss
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid