Implementing Broker with .NET Remoting Using Client-Activated Objects

? Data Column: select for more on pattern organization Application Column: select for more on pattern organization Deployment Column: select for more on pattern organization Infrastructure Column: select for more on pattern organization
Architecture Row: select for more on pattern organization Data Architecture: select for more on pattern organization Application Architecture: select for more on pattern organization Deployment Architecture: select for more on pattern organization Infrastructure Architecture: select for more on pattern organization
Design Row: select for more on pattern organization Data Design: select for more on pattern organization Application Design: select for more on pattern organization Deployment Design: select for more on pattern organization Infrastructure Design: select for more on pattern organization
Implementation Row Data Implementation: select for more on pattern organization Application Implementation: select for more on pattern organization Deployment Implementation: select for more on pattern organization Infrastructure Implementation: select for more on pattern organization
? Complete List of patterns & practices Complete List of patterns & practices Complete List of patterns & practices Complete List of patterns & practices
 

Version 1.0.1

GotDotNet community for collaboration on this pattern

Complete List of patterns & practices

 

Context

You are building an application in .NET that requires the use of distributed objects, the lifetimes of which are controlled by the client. Your requirements include the ability to pass objects by value or reference, whether those objects reside on the same computer, on different computers in the same local area network (LAN), or on different computers in a wide area network (WAN).

 

Implementation Strategy

 

This pattern presents two implementations of client-activated objects in .NET remoting. The main difference between client-activated objects (CAO) and server-activated objects (SAO) is what controls the lifetime of the remote object. In the CAO scenario, the client controls the lifetime; in the SAO scenario, the server controls the lifetime. The example used here is similar in functionality to the example used in Implementing Broker in .NET Using Server-Activated Objects. The first implementation uses client activation as it is described in the .NET documentation and samples. This implementation demonstrates the capabilities of client-activated objects; however, they do have some drawbacks. The second implementation, known as the hybrid approach, resolves these problems.

 

Client-Activated Object Implementation

 

 

 

The RecordingsManager class has a method named GetRecordings, which retrieves a list of recordings from a database and returns the result in DataSet. This class extends the MarshalByRefObject class to ensure that in a remoting scenario a proxy object is used instead of copying the object from the server to the client. The functionality described here is identical to that of the example described in Implementing Broker in .NET Using Server-Activated Objects.

 

 

RecordingsManager.cs

 

 

 

The following sample shows the RecordingsManager class, which is responsible for retrieving DataSet from the database:

 

 

using System;
using System.Data;
using System.Data.SqlClient;

public class RecordingsManager : MarshalByRefObject
{
   public DataSet GetRecordings()
   {
      String selectCmd = "select * from Recording";

      SqlConnection myConnection = new SqlConnection(
         "server=(local);database=recordings;Trusted_Connection=yes");
      SqlDataAdapter myCommand = 
         new SqlDataAdapter(selectCmd, myConnection);

      DataSet ds = new DataSet();
      myCommand.Fill(ds, "Recording");
      return ds;
   }
}
 

 

HttpServer.cs

 

 

 

The following code configures the server to allow for client-activated objects to be created using the new operator. Instead of actually registering an instance (as the SAO example demonstrates), this code configures the server with an application name and the type of the object that will be created. The URL for the remote object is http://localhost:8100/RecordingsServer. Behind the scenes, an SAO is automatically created by the framework on localhost. This SAO is responsible for accepting requests from clients and creating the objects when the client requests them.

 

 

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;


public class HttpServer
{
   static void Main(string[] args)
   {
      HttpChannel channel = new HttpChannel(8100);
      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.ApplicationName = "RecordingsServer";
      RemotingConfiguration.RegisterActivatedServiceType(
         typeof(RecordingsManager));

      Console.WriteLine("Recordings Server Started");
      Console.ReadLine();
   }
}
 

 

HttpClient.cs

 

 

 

To be able to the use the new operator and have the remoting framework create a remote object, as opposed to a local object, you must first associate the type of the remote object with the URL that was specified when the server set the ApplicationName property. This example defines ApplicationName as RecordingsServer and uses port 8100 on localhost.

 

 

using System;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

class HttpClient
{
   [STAThread]
   static void Main(string[] args)
   {
      HttpChannel channel = new HttpChannel();
      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.RegisterActivatedClientType(
         typeof(RecordingsManager), 
         "http://localhost:8100/RecordingsServer");

      RecordingsManager mgr = new RecordingsManager();

      Console.WriteLine("Client.main(): Reference acquired");

      DataSet ds = mgr.GetRecordings();
      Console.WriteLine("Recordings Count: {0}",
         ds.Tables["recording"].Rows.Count);
   }
}
 
Registering the remote object associates the type of the object with the URL. After this occurs, the call to new creates a remote object on the server. This object looks like any other object in the code.

This implementation allows for direct creation of remote objects under the control of the client. It also demonstrates that after the client is configured, object creation is identical to local object creation using the new operator. However, it has a major flaw. You cannot use the shared interface approach described in the SAO pattern. This means that you must ship the compiled objects to the client. For another alternative that uses SoapSuds, see Advanced .NET Remoting [Ingo02].

 

Note: Shipping compiled server objects violates the general principle of distributed objects. It is also undesirable due to deployment and versioning issues.

 

 

To address some of these issues, the following implementation describes a hybrid approach that uses an SAO to create objects. This approach provides the client with the ability to control the lifetime of the object without the server code having to be shipped to the client.

 

 

Hybrid Approach

 

 

 

The hybrid approach involves the RecordingsFactory SAO, which provides methods to create the RecordingsManager CAO. (If you are not familiar with the SAO examples, see Implementing Broker with .NET Remoting Using Server-Activated Objects.) The following class diagram describes the overall solution.

 

Figure 1: Structure of the hybrid approach

 

This implementation uses the shared interface approach described in the SAO examples. The two interfaces, IRecordingsManager and IRecordingsFactory, are in an assembly that is shared between the client and the server. IRecordingsFactory has a single Create method, which returns an object to implement the IRecordingsManager interface. This is an example of the AbstractFactory [Gamma95] pattern. Because the client only depends on the interfaces, there is no need to ship the server code. When a client needs a IRecordingsManager object, it calls the Create method on an instance of IRecordingsFactory. This allows the client to be in control of the lifetime of the IRecordingsManager object without needing its implementation. The two interfaces from the shared assembly are as follows.

 

 

IRecordingsManager.cs

 

 

 

The following sample shows the IRecordingsManager interface:

 

 

using System;
using System.Data;

public interface IRecordingsManager
{
   DataSet GetRecordings();
}
 

 

IRecordingsFactory.cs

 

 

 

The following sample shows the IRecordingsFactory interface:

 

 

using System;

public interface IRecordingsFactory
{
   IRecordingsManager Create();
}
 
The server implementations of these objects, RecordingsFactory and RecordingsManager, are straightforward and are contained in their own assembly, named Server.

 

RecordingsFactory.cs

 

 

 

This class extends MarshalByRefObject and implements the IRecordingsFactory interface:

 

 

using System;

public class RecordingsFactory : MarshalByRefObject, IRecordingsFactory
{
   public IRecordingsManager Create()
   {
      return new RecordingsManager();
   }
}
 
The RecordingsFactory object is the server-activated object. This implementation simply calls new on the RecordingsManager type. This RecordingsManager object is created on the server and is returned, not as a RecordingsManager object, but as the IRecordingsManager interface. This mechanism allows the client to depend on the interface rather than the implementation.

 

RecordingsManager.cs

 

 

 

The only change required in the RecordingsManager class is that it now implements the IRecordingsManager interface.

 

 

using System;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;

public class RecordingsManager : MarshalByRefObject, IRecordingsManager
{
   public DataSet GetRecordings()
   {
      Console.WriteLine("Assembly: {0} - filling a request",
         Assembly.GetEntryAssembly().GetName().Name);

      String selectCmd = "select * from Recording";

      SqlConnection myConnection = new SqlConnection(
         "server=(local);database=recordings;Trusted_Connection=yes");
      SqlDataAdapter myCommand = 
         new SqlDataAdapter(selectCmd, myConnection);

      DataSet ds = new DataSet();
      myCommand.Fill(ds, "Recording");
      return ds;
   }
}
 

 

HttpServer.cs

 

 

 

The server initialization code in the hybrid approach configures the remoting framework for a server-activated RecordingsFactory object. The activation scheme is independent of the channel and the protocol used, so they remain the same as before (HTTP protocol on port 8100).

 

 

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;


public class HttpServer
{
   static void Main(string[] args)
   {
      HttpChannel channel = new HttpChannel(8100);
      ChannelServices.RegisterChannel(channel);

      RemotingConfiguration.RegisterWellKnownServiceType(
         typeof(RecordingsFactory),
         "RecordingsFactory.soap",
         WellKnownObjectMode.Singleton);

      Console.ReadLine();
   }
}
 
In this code, the RecordingsFactory type is associated with the URL: http://localhost:8100/RecordingsFactory.soap.

 

HttpClient.cs

 

 

 

The client code demonstrates the hybrid nature of this approach. You first use the Activator.GetObject method to retrieve the IRecordingsFactory object from the server. Using this server-activated object, you then call the Create method to instantiate an IRecordingsManager object. This newly instantiated object is created on the server but is a remote object.

 

 
using System;
using System.Data;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

public class HttpClient
{
   [STAThread]
   static void Main(string[] args)
   {
      HttpChannel channel = new HttpChannel();
      ChannelServices.RegisterChannel(channel);

      IRecordingsFactory factory = (IRecordingsFactory)
         Activator.GetObject(typeof(IRecordingsFactory),
         "http://localhost:8100/RecordingsFactory.soap");

      Console.WriteLine("Client.main(): Factory acquired");

      IRecordingsManager mgr = factory.Create();
      DataSet ds = mgr.GetRecordings();
      Console.WriteLine("Recordings Count: {0}",
         ds.Tables["recording"].Rows.Count);
   }
}
 

 

Resulting Context

 

Using client-activated objects to implement Broker with .NET remoting results in the following benefits and liabilities:

 

Benefits

 

 

  • Distributed object model. .NET remoting provides a fully featured distributed object model with full common language runtime semantics running on the client and server. You do not lose any fidelity between client and server. The example demonstrated passing the complicated type, System.Data.DataSet, between the client and server. This would not be possible without having the common language runtime on both sides of the connection.
  • Construction parameters. The objects in both the client-activated and hybrid implementations allow for passing constructor arguments when the objects are created.

     

    Liabilities

     

     

  • Remote objects. You cannot forget that these are remote objects. Even though they look like local objects, there is still overhead involved in marshaling data back and forth from the server. Keep in mind that a remote call can be at least 1000 times slower than a local call in the common language runtime. Therefore, you do not want to make more calls than necessary. This desire to minimize round-trips may cause you not to use the finest granularity in regards to the interface.
  • No shared assembly. In the CAO approach, you cannot use the shared assembly approach with interfaces. Instead, you must ship the implementation to the client or use SoapSuds to extract the metadata.
  • Deployment complexity. When using server-activated objects as described in the the hybrid approach, objects must have been registered prior to the client asking for them. This makes deployment more complex.
  • Limited interoperability. You can use .NET remoting to build Web services. However, you must pare down your endpoints to the simplest data types. For example, if you want interoperability with other Web services toolkits, you must restrict parameters to built-in simple types and your own data types (do not use .NET Framework types such as DataSet), and use server-activated objects.
  • More complicated. Compared to Web services, .NET remoting is more difficult to learn, implement, and debug.

     

    Security Considerations

     

    To use the security features available with Microsoft Internet Information Services (IIS) (for example, standard HTTP authentication schemes include Basic, Digest, digital certificates, and even Microsoft .NET Passport) you must use an HTTP-based application hosted in IIS with ASP.NET. Using any other transport protocol or using the HttpChannel outside of IIS requires you to provide a security mechanism.

     

    Operational Considerations

     

    The following is a summary of a performance comparison that appears in the MSDN article, "Performance Comparison: .NET Remoting vs. ASP.NET Web Services" [Dhawan02]. The article concludes that you can achieve the highest performance by using the TCP channel and binary serialization with a Windows Service host. This configuration transmits binary data over raw TCP sockets, which is more efficient than HTTP. Performance is 60 percent faster than with the slowest approach, which is HttpChannel using SOAP serialization hosted in IIS with ASP.NET.

    Hosting in IIS is slower because it involves an extra process hop from IIS (Inetinfo.exe) to Aspnet_wp.exe. However, if you choose to host your channel without IIS and ASP.NET, you will need to provide your own mechanisms for authentication, authorization, and privacy.

     

    Related Patterns

     

    For more information, see the following related patterns:

  • Broker
  • Proxy [Gamma95]

     

    Acknowledgments

     

    [Ingo02] Rammer, Ingo. Advanced .NET Remoting. Apress, 2002.

    [Dhawan02] Dhawan, Priya. "Performance Comparison: .NET Remoting vs. ASP.NET Web Services." MSDN Library, September 2002. Available at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdadotnetarch14.asp.