asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Tracking Services in .NET Remoting and Implementing Your Own Tracking Handlers
By Mansoor Ahmed Siddiqui
Rating: 4.2 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    Tracking Services, as the name implies, allows us to track different activities that are related to an object's life cycle. Since this service is part of the .NET Remoting framework, it would be good to refresh our .NET Remoting concepts.

  • download article source code

    .NET Remoting

    Microsoft's .NET framework provides a way for a client in one application domain to access objects in another application domain; this is made possible through .NET Remoting.

    .NET Remoting is a very flexible technology that allows clients and servers to communicate with each other, no matter where they are located. The client and server can be on the same machine, on different machines on the same network, or in different countries across the world. .NET Remoting makes life easy by providing services and infrastructure components that hide all the complexities from application developers.

    .NET Remoting allows us to use different protocols, different serialization formats, configuration settings, etc. .NET Remoting even allows programmers to create and install their own "hooks" in the .NET Remoting pipeline in order to customize the default behavior of the remoting framework (Tracking Services is an example of such a hook)

    So how does .NET Remoting work? Well, some of the concepts behind .NET Remoting are borrowed from the old distributed technologies, such as DCOM, CORBA, etc. First of all, always remember that in .NET Remoting, objects are exposed to the outside world through some "Listener" process; that listener process could be a console application, a WinForm application, a Windows service, or even IIS. The listener process specifies the objects that are exposed programmatically or by using the .config file. Once that part is done, client applications connect to the listener process for object creation requests and the listener process then creates and returns the objects back to the client. All communication between client and server application domains is performed by sending and receiving encoded streams of data over some channel.

    Channels

    The basic purpose of channels is to transport data from one end point to another; .NET comes with two built-in channels, HTTP channel (System.Runtime.Remoting.Channels.Http) and TCP channel (System.Runtime.Remoting.Channels.Tcp).

    TCP channel is faster than HTTP channel, and it is good for binary data; whereas HTTP channel is very good when it comes to firewalls and the Internet. You are not limited to these channels; you can create your own custom channels and plug them into .NET Remoting.

    Formatters

    Formatters, as the name implies, format data or messages for delivery. Once data/message has been formatted, it is transported to other application domains by using the appropriate channel. .NET comes with two built-in formatters, Soap formatter (System.Runtime.Serialization.Formatters.Soap) and Binary formatter (System.Runtime.Serialization.Formatters.Binary).

    Soap formatter formats message in Soap 1.1 specification, whereas Binary formatter formats message in pure binary form.

    Types of Remoting Objects

    There are two types of objects supported by .NET Remoting: server-activated objects (also known as well-known objects) and client-activated objects.

    Server-activated objects

    Server-activated objects, as the name implies, are created by the server and their lifetime is also managed by the server. The point to catch here is that these objects are not created when a client calls New or Activator.GetObject; rather, the actual instance of the object is created when the client actually invokes a method on proxy.

    There is one implication to the above mentioned behavior. Since the object is not created at the time the client calls the New or Activator.GetObject method, we cannot use non-default constructors with server-activated objects. Only default constructors (constructors with no parameters) are supported for such objects.

    There are two modes in which server-activated objects can be activated:

    • Singleton
      Only one object will be created on the server to fulfill the requests of all the clients; that means the object is shared, and the state will be shared by all the clients.

    • SingleCall
      Such objects are created on each method call and objects are not shared among clients. State should not be maintained in such objects because such objects are destroyed after each method call.

      Client-activated objects

      Client-activated objects are created by the server and their lifetime is managed by the client. In contrast to server-activated objects, client-activated objects are created as soon as the client calls New or any other object creation method; therefore, we can use both the default and non-default constructors with client-activated objects. Client-activated objects are specific to the client, and objects are not shared among different clients; object instance exists until the lease expires or the client destroys the object.

      Configuration

      Like other .NET technologies, .NET Remoting also needs some configuration information in order to work. We can configure our remoting application either programmatically or by using a .config file. For this article, we will discuss .config files.

      .NET Remoting configuration settings are packed in the <application> tag in the following XML format:

      
      <configuration>
        <System.Runtime.Remoting>
          <application>
      
          </application>
        </System.Runtime.Remoting>
      </configuration>
      
      
      In order to expose server-activated objects, we use the <service> tag, as shown in the example below:
      
      <configuration>
       <system.runtime.remoting>
        <application>
         <service>
          <wellknown mode="SingleCall" type="MyObjects.Customer, MyObjects"
          objectUri="Customer"/>
         </service>
         <channels>
          <channel ref="tcp" port="1234"/>
         </channels>
        </application>
       </system.runtime.remoting>
      </configuration>
      
      
      The above configuration specifies that we are exposing a SingleCall object whose complete type name is MyObjects.Customer. The class is contained in the MyObjects.DLL assembly; the object's URI is Customer, and the object is accessible through TCP channel on port 1234.

      Each server-activated object will have its own <wellknown> tag.

      The corresponding client-side .config file would be as follows:

      
      <configuration>
       <system.runtime.remoting>
        <application name="MyClient">
         <client>
          <wellknown type="MyObjects.Customer,MyObjects"
            url="tcp://localhost:1234/Customer"/>
         </client>
         <channels>
          <channel ref="tcp"/>
         </channels>
        </application>
       </system.runtime.remoting>
      </configuration>
      
      
      The client-side configuration file is slightly different from the server configuration file. We are using the <client> tag instead of the <service> tag; similarly we are using the url attribute in the <wellknown> tag on the client-side to specify the exact location of the server object.

      For client-activated objects, we use the <activated> tag (both on sever and the client), instead of the <wellknown> tag. Following example shows how to expose a client-activated object.

      
      <system.runtime.remoting>
        <application name="Data">
         <service>
          <activated type="MyObjects.Customer,MyObjects"/>
         </service>
         <channels>
          <channel ref="tcp" port="1234"/>
         </channels>
        </application>
       </system.runtime.remoting>
      </configuration>
      
      
      The corresponding client-side configuration file would look as follows:
      
      <configuration>
       <system.runtime.remoting>
        <application name="Data">
         <client url="tcp://localhost:1234/Data">
          <!-- You can only use one url @ a time -->
          <activated type="MyObjects.Customer,MyObjects"/>
         </client>
         <channels>
          <channel ref="tcp"/>
         </channels>
        </application>
       </system.runtime.remoting>
      </configuration>
      
      

      .NET Tracking Service

      .NET Tracking Service provides a way to plug our own code in the .NET Remoting infrastructure in order to receive the notifications about object marshaling. For example, the .NET Tracking Service will raise an event when an object gets marshaled to another application domain, etc. .NET Tracking Service raises some events that are related to the marshaling process of Marshal-by-Ref (MBR) objects. A component that is interested in these events can register with the .NET Remoting infrastructure in order to receive those events.

      .NET Tracking Service lends us the flexibility to monitor and analyze a running application and observe the application patterns in order to see how our application is behaving under certain workloads, how many objects are getting created during peak time, etc. .NET Tracking Service provides us with lots of helpful information during those events; we can log that information for trend analysis and other observations.

      There is only one restriction with .NET Tracking: It works for Client-activated objects only!

      Like other .NET pluggable architectures, creating a component that utilizes the .NET Tracking Service is very easy. There are two simple steps that we have to follow:

      • Create a .NET class that implements the ItrackingHandler interface (we'll call such a class Tracking Handler)
      • Register that class with the .NET Remoting infrastructure by using the TrackingServices class.
      I'll explain in detail.

      ITrackingHandler Interface

      ItrackingHandler interface is part of System.Runtime.Remoting.Services namespace and any class that implements this interface can be used to receive object marshaling events. This interface has following methods:

      Method

      Description

      MarshaledObject

      This method is called to notify tracking handlers that an object is being marshaled.

      UnmarshaledObject

      This method is called to notify tracking handlers that an object is being unmarshaled.

      DisconnectedObject

      This method is called to notify tracking handlers that an object is being disconnected (for example object lease has been expired)

      Implement this interface in your class, as shown below:

      
      Public class MyTrackingHandler : ITrackingHandler
      {
         public void MarshaledObject(object obj, ObjRef or)
         {
           // Write your code here; most probably you would log the 
           // information
         }
         
         public void UnmarshaledObject(object obj, ObjRef or)
         {
           // Write your code here; most probably you would log the 
           // information
         }
          
         public void DisconnectedObject(object obj)
         {
           // Write your code here; most probably you would log the 
           // information
         }
      }
      
      
      You can see from the code that both the MarshaledObject and UnmarshaledObject methods receive two parameters: an object and an ObjRef; whereas DisconnectedObject receives only one parameter: an object. All object parameters contain a reference to the object that is being marshaled, unmarshaled, or disconnected; whereas all ObjRef contain a reference to an object that contains all the information that is required to generate a proxy in the client application domain. The ObjRef contains information such as the Type of the server object, object's location on the server, and other very important connection and transport related information critical for the proxy.

      TrackingServices Helper Class

      TrackingServices is a helper class that is part of the System.Runtime.Remoting.Services namespace; this class is used to manipulate the tracking handlers. Following are the important methods of this class:

      Method

      Description

      RegisterTrackingHandler

      Adds a tracking handler in the list of handlers that are interested in receiving the object marshaling events.

      UnregisterTrackingHandler

      Removes a tracking handler from the list of handlers that are interested in receiving the object marshaling events.

      RegisteredHandlers

      Returns an array of ITrackingHandler type that contains references to the currently registered tracking handlers.

      Following code shows how to register our own tracking handler with the .NET Remoting infrastructure:

      
      MyTrackingHandler objHandler = new MyTrackingHandler() ;
      TrackingServices.RegisterTrackingHandler(objHandler) ;
      
      
      Once our handler is registered, we will be receiving the events. In order to stop receiving events, we would have to call UnregisterTrackingHandler method, as shown below (assuming that objHandler points to a previously registered tracking handler):
      
      TrackingServices.UnregisterTrackingHandler(objHandler) ;
      
      
      Enumerating through all the registered tracking handlers is also easy. Following code gets the list of currently registered tracking handlers and iterates through them:
      
      ITrackingHandler[] objHandlers = TrackingServices.RegisteredHandlers ;
      foreach (ITrackingHandler objHandler  in objHandlers)
      {
       // Use objHandler here
      }
      
      

      Let's Do Something Practical

      Now that we are familiar with all the background information we need for building our own custom tracking handler, it is time to write one.

      The custom tracking handler that we are going to build will log all the information in a SQL Server database table. This will allow us to analyze the data and observe the object creation and marshaling behavior of our application. Some of the things that we can analyze from such data are:

      • Number of objects that were being marshaled, unmarshaled, or disconnected during a period of time
      • State of each marshaled, unmarshaled, and disconnected object
      • The URI that was used to instantiate the object
      These are the projects in the sample code:

      • MyObjects
      • TrackingHandlers
      • TrackingListener
      • TrackingClient

      Apart from the above projects, there is one database.sql file that contains SQL script for creating an SQL server database, database table, and stored-procedure.

      MyObjects.DLL

      MyObjects assembly contains a class named Customer that will be exposed to the remoting clients through TrackingListener.exe.

      Customer is a simple C# class that overrides the default ToString method so that we can get our hands on the internal state of the object. We could have marked the object as serializable by using the [serializable] attribute or could have implemented the Iserializable interface in order to get the state of the object, but I opted for the ToString method.

      The following is the code of the Customer class.

      
      public class Customer : MarshalByRefObject
      {
        private string mstrName ;
        private string mstrAddress ;
        private string mstrPhone ;
        private DateTime mdtDOB ;
        public Customer()
        {
          //
          // TODO: Add constructor logic here
          //
          mstrName = "Mansoor Siddiqui" ;
          mstrAddress = "9999 Street, Apt 9, City State, USA" ;
          mstrPhone = "999-999-9999" ;
          mdtDOB = DateTime.Parse("01/01/1980") ;
         }
      
         public override string ToString()
         {
          string strState ;
          strState = Name + "|" + Address + "|" + Phone + "|" + 
           DOB.ToLongDateString() ;
          return strState;
         }
      
         // Rest of the code has been skipped due to length
      }
      
      

      TrackingHandlers.DLL

      TrackingHandlers assembly contains our custom tracking handler named MyTrackingHandler. As explained above, we have to implement the ItrackingHandler interface in order to make our class a tracking handler.

      The following is the code of our custom tracking handler.

      
      public class MyTrackingHandler : ITrackingHandler
      {
       private Database mobjDatabase ;
       private string mstrConnectionString ;
      
       public MyTrackingHandler()
       {
       //
       // TODO: Add constructor logic here
       //
        mobjDatabase = new Database() ;
        mstrConnectionString = "Data Source=localhost;uid=sa;pwd=;Initial 
      Catalog=TrackingDB" ;
       }
      
       public void MarshaledObject(object obj, ObjRef or)
       {
        object[] arrobjParameters = new Object[7] ;
        arrobjParameters[0] = "M" ;
        FillParameters(arrobjParameters, obj, or) ;
        mobjDatabase.ExecuteCommand(mstrConnectionString, 
                                 "usp_Add_TrackingLog", arrobjParameters) ;
       }
      
       public void DisconnectedObject(object obj)
       {
        object[] arrobjParameters = new Object[7] ;
        arrobjParameters[0] = "D" ;
        FillParameters(arrobjParameters, obj, null) ;
        mobjDatabase.ExecuteCommand(mstrConnectionString, 
                                  "usp_Add_TrackingLog", arrobjParameters) ;
       }
      
       public void UnmarshaledObject(object obj, ObjRef or)
       {
        object[] arrobjParameters = new Object[7] ;
        arrobjParameters[0] = "U" ;
        FillParameters(arrobjParameters, obj, or) ;
        mobjDatabase.ExecuteCommand(mstrConnectionString, 
                                   "usp_Add_TrackingLog", arrobjParameters) ;
       }
      
       private void FillParameters(object[] r_objArray, object obj, 
      ObjRef or)
       {
        r_objArray[1] = DateTime.Now ;
        r_objArray[2] = obj.ToString() ;
        r_objArray[3] = obj.GetHashCode() ;
        if (or != null)
        {
         if (or.TypeInfo != null)
         {
           r_objArray[4] = or.TypeInfo.ToString() ;
           r_objArray[5] = or.TypeInfo.TypeName ;
         }
         else
         {
           r_objArray[4] = "" ;
           r_objArray[5] = "" ;
         }
      
         if (or.URI != null)
           r_objArray[6] = or.URI.ToString() ;
         else
           r_objArray[6] = "" ;			
        }
        else
        {
         r_objArray[4] = "" ;
         r_objArray[5] = "" ;
         r_objArray[6] = "" ;
        }
       }
      
      
      The code in MarshaledObject, UnmarshaledObject and DisconnectedObject methods simply save the information into database.

      TrackingListener.exe

      TrackingListener.exe is the server-side listener, which creates and installs the tracking handler in the .NET Remoting pipeline and fulfills the object creation requests.

      The following is the TrackingListener class code.

      
      class TrackingListener
      {
        [STAThread]
        static void Main(string[] args)
        {
         RemotingConfiguration.Configure("TrackingListener.exe.config") ;
         TrackingServices.RegisterTrackingHandler(new 
          				    TrackingHandlers.MyTrackingHandler()) ;
         Console.WriteLine("Press enter to terminate ...") ;
         Console.ReadLine() ;
        }
      }
      
      
      The code is very simple. RemotingConfiguration.Configure method is called by the applications that use .config files; calling this method configures the application according to the settings specified in the configuration file.

      The configuration settings for our listener process are specified in TrackingListener.exe.config file. Following is the snippet from the .config file:

      
      <configuration>
       <system.runtime.remoting>
        <application name="TrackingTest">
         <service>
          <activated type="MyObjects.Customer, MyObjects"/>
         </service>
         <channels>
          <channel ref="tcp" port="9000"/>
         </channels>
        </application>
       </system.runtime.remoting>
      </configuration>
      
      
      You can see that the exposed object is client-activated, and it is accessible through TCP channel port 9000.

      TrackingClient

      TrackingClient is a WinForm application that simply creates an instance of the Customer class in order to generate some activity on the server.

      Testing the Tracking Handler

      In order to test the tracking handler, we have to perform the following tasks.

      First of all, run the database script in order to create the database and related database objects. This article assumes that SQL Server in also running on the same box as that of the listener, but if SQL Server is on another machine, then you would have to change the connection string in the tracking handler class.

      A better solution would be to store the connection string in the .config file of the listener process and the listener should pass that string to handler via constructor.

      Run the listener process, TrackingListener.exe, from the directory where you unzipped the sample code; the listener process will register appropriate channels and a console window will be opened. Now our Customer object is ready to be created.

      Launch TrackingClient.exe and press the Create object button on the dialog box; this will instantiate a Customer instance and will cause the .NET Remoting infrastructure to call our tracking handler's MarshaledObject method. Once the object is created successfully, go and look at the TrackingLog table in the database. On my machine, I had the following rows in the database table:

      In order to test the disconnect event, just wait 5 minutes (because 5 minutes is the default lease time for client-activated objects, after that objects are disconnected from the proxy). On my machine,the following rows were in the database:

      Note that I am storing "M", "U", or "D" in the activity column of the database table; these values represent the activity (Marshaled, Unmarshaled, or Disconnect) that occurred.

      Conclusion

      .NET framework and all of its related technologies allow us to plug our own hooks in their processing pipeline; .NET tracking handlers is an example of such a hook. As you have seen, it is very easy to write tracking handlers, but we should have background information about .NET Remoting, different object types, configuration files, and other remoting intricacies.

      About the Author

      Mansoor Ahmed Siddiqui is a software consultant and technical writer working in the United States. He has a masters degree in computer science and been involved in software development since 1997. His areas of expertise include designing and developing Web-based applications, client-server applications, and n-tier applications, with a special focus on middle-tier and Win32 programming.

      He has expertise in Microsoft. NET Framework, ASP.NET, C#, Visual Studio .NET, Web Services, ADO.NET, ASP, JavaScript, eXtensible Markup Language (XML), Simple Object Access Protocol (SOAP), Visual C++, Microsoft Foundation Class Library (MFC), Active Template Library (ATL), Visual Basic 6.0, ActiveX Data Objects (ADO), COM/DCOM/COM+, Microsoft Transaction Server (MTS), Microsoft Message Queue (MSMQ), SQL Server 7.0/2000, OMG's Unified Modeling Language (UML), Rational Software Corp.'s Rational Rose, Java, Java Server Page (JSP), servlets, Enterprise JavaBeans (EJBs), Java 2 Platform Enterprise Edition (J2EE).

      Currently, he is working with Visual Studio 7.0 and the Microsoft .NET platform. He is an MCSD and is Brainbench certified in different languages.

      Apart from technical writing, other interests include listening to music, swimming, playing cricket and pool, and hanging out with friends. He can be reached at mansoorasiddiqui@hotmail.com and ICQ 151707288.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Jul 28, 2005 - N-Tier Web Applications using ASP.NET 2.0 and SQL Server 2005 - Part 2
    In the second part of his series on building N-tier web applications using ASP.NET 2.0 and SQL Server 2005, Thiru Thangarathinam covers the business logic and user interface layers. In the process, he also examines some new features in ASP.NET 2.0 that greatly simplify the development process.
    [Read This Article]  [Top]
    Jul 14, 2005 - An Innovative Technique for Creating Reusuable Page Templates in ASP.NET 1.x
    Code reusuability is one of the major goals of any good object-oriented programmer. While the ASP.NET framework has made code reusuability easier and more elegant than it was in classic ASP, one area where reusuability could be improved is at the UI level. This article outlines a technique that you can use in ASP.NET 1.x that allows every page in your web application to inherit not only the functionality of a base page, but its UI as well.
    [Read This Article]  [Top]
    Jun 23, 2005 - Monitoring SharePoint Usage through an ASP.NET Web Application
    In this article, Gayan Peiris looks at creating an ASP.NET web application that will display the usage details of a selected SharePoint site. Building such an application enables SharePoint administrators to gather all SharePoint usage data from a central location.
    [Read This Article]  [Top]
    May 12, 2005 - Retrieving SharePoint Site Information in an ASP.NET Web Application
    In this article, Gayan Peiris examines using the SharePoint Object Model to access SharePoint site information from an ASP.NET web application. It should be of particular interest to SharePoint administrators who can use the included code as a starting point for development of their own web-based SharePoint administration application.
    [Read This Article]  [Top]
    Dec 23, 2004 - Automated Deployment for Side By Side .NET Web Apps for Visual Studio .NET 2003
    In this article, David Every outlines a step-by-step account of how he solved the problems he encountered while implementing an auto-deployment process. He also describes how to create a stable process for automated remote .NET deployment featuring "side-by-side" capability.
    [Read This Article]  [Top]
    Sep 29, 2004 - Developing Web Parts with ICellConsumer Interface
    Most default SharePoint Server Web Parts can be connected across organizations. The third article in this series shows how to develop connectable Web Parts that consume information provided by other Web Parts.
    [Read This Article]  [Top]
    Aug 10, 2004 - Implementing and Promoting Daily Builds
    Automatic daily builds is a well known software engineering best practice. This article introduces a strategy for implementing and promoting daily builds and offers tips and tricks for preventing and fixing breaks.
    [Read This Article]  [Top]
    Jun 21, 2004 - Using Open Source .NET Tools for Sophisticated Builds
    Building an application can be more than pressing F5. With an increasing number of quality packages being released, developers for the .NET platform now have options to create a very sophisticated build process. Aaron Junod describes a sample build environment and shows how a number of tools can work together to make reliable, predictable, and value-added builds.
    [Read This Article]  [Top]
    Jun 24, 2003 - Programming for the Palm Part 1 - Creating the Palm Application
    The first part of this three part series walks through the process of creating a mobile blog application using a BASIC development environment for Palm OS devices called NS Basic. Subsequent articles will focus on synchronizing the data to the desktop using C# and creating an installer.
    [Read This Article]  [Top]
    Jun 18, 2003 - Online Database Functions Testing Tool
    This short article provides source code for a classic ASP online database functions testing application and shows how to configure and use the tool for either SQL Server or Oracle.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers