Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM+ and .NET - A practical approach - Part 3

0.00/5 (No votes)
2 Mar 2004 1  
A look at COM+ and .NET

(f) Notify others when your application change state

Loosely Coupled Events (LCE) is one of the COM+ features that can be very useful. LCE enable COM+ component to initiate event that eventually will be raise on unknown clients. Clients should be register as COM+ event component subscriber, by using COM+ MMC or dynamically. Publisher calls one of the COM+ event components methods that cause event to be send and rose in all of the subscribers. The contract between event component subscribers and publisher is single interface that should be implements by all of the contract parties.

LCE can be used to create notification to components or application without knowing in advance who will be those applications or components. In order to enable those applications or components to get notifications they need to implement that event interface and subscribe to the COM+ event component. Every other task�s that should be dome are being taken care of by COM+.

To simulate the power of LCE we will create mechanism that enable desire web application to get notification when certain Database table changed by one of web applications or any other application. To enable that mechanism we need to control all of the applications access to database. This task can be achieved by creating new COM+ server application that implement Select, Insert, Update and Delete functions. Every data layer class in every application should use this class when sending SQL statements to database. That database pipe class is actually the publisher. Every database command will end up with call to class register as COM+ library and serve as the event class. Every web application that wants to be notifies needs to implement subscriber class and register it (subscriber class) as the event subscriber.

Let's start with the event component � LCEventLibrary.Dll. It's a simple assembly containing the interface and a class that register as COM+ library serving as event class using EventClass attribute. Note that this class shouldn�t implement anything and shouldn�t be call directly. This class is used by COM+ to call subscribers.

Figure 5.0

Sample screenshot

public interface IMySink
{
   void OnDBChange(string Action, string DBEntity);
}
[EventClass(FireInParallel = true)]
public class MyEventClass : ServicedComponent,IMySink
{  
   public void OnDBChange(string Action, string DBEntity)
   {
      throw(new 
NotImplementedException("You should not call an event class directly! "));
   }
}

To enable Web application subscription to MyEventClass we will create service class (TransientSubscription) that encapsulates all the lines that should be write to add subscriber dynamically. Dynamic subscribing can be done by manipulating COM+ metadata. To manipulate COM+ metadata we need to use COM+ COM + 1.0 Admin Type Library so I use tlbimp.exe to create managed wrapper for that COM library. TransientSubscription expose static function to add and remove subscribers. To add subscriber one should send unique name, the event class type and subscriber class. Sink interface can be found by looping through subscriber class interfaces.

static public void Add(string subName,Type eventClass,Type sinkInterface,
string method,object subscriber)
{ 
   Type sinkType = subscriber.GetType(); 
   if(method != "")
   {
      MethodInfo info = sinkInterface.GetMethod(method); 
   } 
   //Adding a new transient subscription to the catalog

   ICOMAdminCatalog catalog;
   ICatalogCollection transientCollection; 
   ICatalogObject subscription = null;
   catalog = (ICOMAdminCatalog)new COMAdminCatalog();
   transientCollection = 
(ICatalogCollection)catalog.GetCollection("TransientSubscriptions");
   subscription = (ICatalogObject)transientCollection.Add();
   subscription.set_Value("Name",subName);
   subscription.set_Value("SubscriberInterface",subscriber);
   string eventClassString = "{"+eventClass.GUID.ToString()+"}";
   subscription.set_Value("EventCLSID",eventClassString);
   string sinkString = "{" +sinkInterface.GUID.ToString()+ "}";
   subscription.set_Value("InterfaceID",sinkString);
   subscription.set_Value("MethodName",method);
   subscription.set_Value("FilterCriteria","");
   subscription.set_Value("PublisherID","");
   transientCollection.SaveChanges();
} 

The subscriber class (MySubscriber) is implemented as part of the web application. MySubscriber implements IMySink interface and write the code that should be called when the event class calls its subscribers. In this demo I simply update application variable that something changed. One problem that needs workaround is updating Application object. I can�t use HTTPContext.Current since subscriber call isn�t connect to any request. To workaround that problem I add constructor that get HttpApplicationState as parameter and set internal member to it.

public class MySubscriber : LCEventLibrary.IMySink 
{
   private System.Web.HttpApplicationState App;
   public MySubscriber()
   {
   }
   public MySubscriber(System.Web.HttpApplicationState inApp)
   {
      App = inApp;
   }
   public void OnDBChange(string Action, string DBEntity)
   {
      App["DbChange"] = true; 
   }
}
 

The actual subscribing happened in Application_start event.

m_Subscriber = new MySubscriber(System.Web.HttpContext.Current.Application);
TransientSubMgr.TransientSubscription.Add
("MySub",typeof(LCEventLibrary.MyEventClass),m_Subscriber);
Application["DbChange"] = false;

PublisherClass assembly contains PubClass class that act as the publisher. PubClass serve as single connection point to database thus register as COM+ application with methods for all database actions. Every method create instance of MyEventClass based on IMySink Interface and call OnDBChange with right parameters. UpdateData extract table name from SQL statement and send it as parameter.

public class PubClass : System.EnterpriseServices.ServicedComponent 
{
   public PubClass()
   { 
   }
   public void UpdateData(string SQL)
   {
      LCEventLibrary.IMySink sink;
      sink = new LCEventLibrary.MyEventClass();
      string[] arr = SQL.Split (' ');
      sink.OnDBChange("Update",arr[1]);
   }

To activate the publisher I add new page (CallPublisher.aspx) to the web application. That page contain two buttons one for calling PubClass UpdateData Method and one to simply refresh the page. On page_load I check for application DbChange variable. If it set to true one of the database actions execute and Notification print into the render HTML.

   private void Page_Load(object sender, System.EventArgs e)
   {
      if ((bool)Application["DbChange"] == true)
      {
         Response.Write ("DB data changed!");
      }
   }
   private void Button1_Click(object sender, System.EventArgs e)
   {
      PublisherClass.PubClass oPC = new PublisherClass.PubClass();
      oPC.UpdateData("update MyTable set MyField=value"); 
   }
   private void Button2_Click(object sender, System.EventArgs e)
   { 
   }
 

Running the page for the first time will show the form with just the two buttons. Clicking on Update button will cause publisher to call event class method followed by calling to all subscribers. The subscriber class in that web application will update given application variable. Pushing the refresh button now will end up with page containing text notification about the change.

(g) Picking COM+ fruit without need to nourish COM+ tree.(COM+ 1.5 / CLR 1.1)

COM+ 1.5 introduce new working paradigm that enables classes builders to use part of COM+ services without registering classes as COM+ application (Services Without Components - SWC). That ability introduced to C++ developers that performance is their main concern. C++ developers can use CServiceConfig class to query interface of one of the services and then use APIs to use the chosen COM+ services as inline block or in batch mode.

Although SWC can be used by .NET application using interop CLR 1.1 introduce new class (ServiceConfig) in EnterpriceServices namespace that enable using COM+ SWC using .NET objects. ServiceConfig simply encapsulate the needed API functions to enable SWC. Using of this class if straight forward. You need to create an instance, set the features that you want to use by setting attributes, use Enter and Leave to mark the block that will use those COM+ features and write code inside that block.

   public void HandleUser(string UID)
   {
      DataLayerClass oDLC = new DataLayerClass();
      try 
      { 
         ServiceConfig sc = new ServiceConfig();
         sc.Transaction = TransactionOption.RequiresNew;
         sc.TransactionTimeout = 30;
         sc.TrackingAppName = "SWC Test";
         sc.TransactionDescription = "Test"; 
         sc.TrackingEnabled = true;
         //start block

         ServiceDomain.Enter(sc); 
         // cal data layer to set DB

         oDLC.DeleteUser(UID); 
         ContextUtil.SetComplete(); 
      }
      catch(Exception ex) 
      { 
         ContextUtil.SetAbort();
      }
      finally 
      {
         // end block

         ServiceDomain.Leave();
      }
   }
 

As I mention SWC enable just part of COM+ services. To be more accurate those are COM+ services that might be use by SWC:

  • COMTIIntrinsic.
  • IISIntrinsic.
  • Context switching.
  • Isolation level.
  • Partitions
  • Side by side assemblies
  • Synchronization
  • Thread pool
  • Transactions.

Although its long list of attributes the most useful services such as Object pooling, JITA, Queued Components and Loosely coupled events aren�t include. As you saw throughout this article those COM+ features used to solve daily problems and to turn your application to be more robust and available.

(h) Notify ASP.NET from COM+ server application.

If you convinced that registration Class as COM+ server application can turn your application to become more stable and available you need also to remember the limitations that I already mention. One cause for limitation is the need to use Remoting between the COM+ component and the caller application. If you call COM+ component Remoting is done by CLR for you but if you need to call Application from COM+ application some work should be done in order to enable it.

One of the common scenarios is call back events from COM+ application to the calling application. When COM+ application needs to call your application its now your application turns to serve as Remoting server. To enable your application to behave as Remoting server you need to register to TCP port.

Let�s take a real life situation and see how we can do it with COM+ server applications and callback events. Suppose you need to read from database very large amount of data, manipulate that data and display all the data to the user. If we will follow the default page process paradigm we will get all the data format it and send it to the client in single buffer. We will supply very slow application to the end user. When we need to deal with large amount of data its better to read chunk of total data, format it and send it directly to the user. Using this paradigm will give the use sense of well operated system. The user quickly can see some data and as the time grows he can see more and more data until all the data send.

To enable that page process sequence we need to A- set buffering to false, so we can send chunks of data. B- Read data from Database using DataReader, collect chunk of records, format them and send them to the client. To make our application more stable its better to put data reading in class that will be register as COM+ application. To enable COM+ components to send chunks of data to the application we will implement call back events that will be notify the caller when chunk of data ready to be format and send.

The sample project is made from new web application (ASPNET_COMPLUSE_CALLBACK), regular assembly (ComPlusFacade) and COM+ server application class (ComPlusLib). ComPlusFacade task is to get the COM+ component events and write the received data to output buffer. ComPlusFacade is separate assembly because both the caller application and called COM+ component needs to be in GAC to enable each and every one of them to use other. Before running this sample, don�t forget to register those assemblies in GAC.

Figure 6.0

Sample screenshot

ComPlusLib contain two classes: XMLArg that decorate with Serializable attribute to enable transfer of event argument over Remoting channel.

   [Serializable()]
   public class XMLArg : EventArgs
   {
      string XMLBuffer; 
      public XMLArg()
      { 
      }
      public XMLArg(string XMLBufferArg)
      {
         this.XMLBuffer = XMLBufferArg;
      }
      public string XMLBufferArg
      {
         get
         {
            return this.XMLBuffer;
         }
         set
         {
            this.XMLBuffer = value;
         }
      }
      public override string ToString()
      {
         return this.XMLBuffer;
      }
   }

clsComPlusLib is typical COM+ component contained declared event that fire every loop through for statement inside ProcessData method.

   [ProgId("clsComPlusLib"),
   Transaction(TransactionOption.NotSupported), 
   MustRunInClientContextAttribute(false),
   EventTrackingEnabledAttribute(true), 
   JustInTimeActivation(true),
   Synchronization(SynchronizationOption.Required),
   Serializable
   ]
   public class clsComPlusLib : ServicedComponent
   {
      public new event EventHandler DataArrive;
      public clsComPlusLib()
      {
      }
      public void ProcessData()
      {
      for (int i=0 ; i<100000 ; i++)
      {
         EventArgs XML = new XMLArg ("i=" + i + "<br>\n");
         this.DataArrive(null,XML);
      }
   }
}

ComPlusLib will be register in COM+ by CLR you just need to register it in GAC by drag and drop or gacutil.exe.

ComPlusFacade is the interesting part in this sample. ComPlusFacade is the glue between WEB application and COM+ server application. FacadeObjByRef also decorated with Serialization to enable transfer the class over Remoting. To enable FacadeObjByRef to serve as Remoting server it derived from MarshalByRefObject and declared TcpChannel type member.

[Serializable()]
public class FacadeObjByRef : MarshalByRefObject
{
   protected ComPlusLib.clsComPlusLib eventHandler = 
new ComPlusLib.clsComPlusLib();
   protected System.IO.Stream outStream;
   public static TcpChannel theChannel; 

Class contractor get Stream as a parameter. The send stream should be page response output stream, that will be use to write received data to the client. Inside the constructor I register TcpChannel to let the remoting runtime choose an available port and set event handler to COM+ component event.

   public FacadeObjByRef(System.IO.Stream outStream) 
   {
      lock(this)
      {
         if (theChannel == null)
         {
            theChannel = new TcpChannel(0); 
            ChannelServices.RegisterChannel(theChannel);
         }
      }
      if (textEncoder == null)
      {
         textEncoder = Encoding.UTF8;
      }
      this.outStream = outStream;
      this.eventHandler.DataArrive += new 
System.EventHandler(this.DataArive);
   }

ProcessData is the class public method that will be called by WEB page and calls COM+ application synchronously to start its work.

public void proccessData()
{
   eventHandler.ProcessData();
   this.CloseStream();
   this.cleanUp();
}

While COM+ application does its work an event is raising and handling by DataArrive. DataArrive converts event argument to byte array and send that byte array to the client using internal member that holds calling page response stream buffer.

public void DataArive(object sender, System.EventArgs e)
{
   byte[] toWrite = textEncoder.
GetBytes(((ComPlusLib.XMLArg)e).XMLBufferArg + "\n");
   outStream.Write(toWrite, 0, toWrite.Length);
}
 

ComPlusFacade run under web application process you just need to register it in GAC.

ASPNET_COMPLUSE_CALLBACK web application got just one aspx page (webform1.aspx) that disable default buffering behavior and create object from FacadeObjByRef class and call ProcessData method.

private void Page_Load(object sender, System.EventArgs e)
{
   Response.BufferOutput = false; 
   ComPlusFacade.FacadeObjByRef oObj = new 
ComPlusFacade.FacadeObjByRef (Response.OutputStream );
   oObj.proccessData ();
}
 

What left to be done us to run ASPNET_COMPLUSE_CALLBACK and to see its behavior. As I explain the page is quickly up showing chunks of data that keeps arriving and adds to the browser HTML.

Conclusion

This article aim is to show how you can use COM+ services to solve common programming tasks while developing web applications. We start by seeing the impact of COM+ server and library application on performance and what are the limitations derived from server application usage.

After understanding performance consequences I try to explain and show what is the tremendous contribute of using COM+ server application to applications stability, robust and monitoring. By solving daily task such as long component initialization, long components task, notification and other we see how we can use COM+ services and especially COM+ server services save code writing and programmers efforts.

COM+ can supply powerful services that can help you to create quickly sophisticated and stable application. The main drawback of using COM+ services is performance penalty. We saw that we can use part of COM+ services with Services without Components without penalty and COM+ library application with acceptable penalty. COM+ server that provides most of the interesting services has bad influence over performance mainly due to its usage of DCOM.

Before using COM+ services its better to create concept application and to test it with ACT to see if using COM+ server will fit application performance goals. I hope that next versions of window such as Longhorn will ship with COM+ services that won�t be based on DCOM and will enable more applications to use COM+ server application.

Previous Parts

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