12.7 Make an Object Remotable


Problem

You need to create a class that can be accessed from another application or another computer on the network. However, you don't need cross-platform compatibility, and you want optimum performance.

Solution

Make the class remotable by deriving from System.MarshalByRefObject , and create a component host that registers the class with the .NET Remoting infrastructure.

Discussion

Remoting allows you to make an object accessible across process and machine boundaries. While XML Web services are ideal when you need to share functionality across platforms or trust boundaries, Remoting is the best performing choice for a closed system in which all components are built on .NET and the Windows operating system.

To use .NET Remoting, you need the following ingredients , each of which must reside in a separate assembly:

  • A remotable object This object can be accessed from other applications and computers and must derive from the System.MarshalByRefObject .

  • A component host This application registers the remotable type with the .NET Remoting infrastructure using the RemotingConfiguration class from the System.Runtime.Remoting namespace. As long as the component host is running, remote clients can create instances of the remotable object.

  • A client application This application can create instances of the remotable class in the component host process and interact with them. The client uses the RemotingConfiguration class to register the types it wants to access remotely.

Figure 12.3 shows how these three parts interact. In this example, there's only one client. However, it's also possible for multiple clients to create instances of the remotable class at the same time. In this case, each client will have its own remotable object instance, and all the objects will reside in the application domain of the component host.

click to expand
Figure 12.3: Using a remotable class.

The first step is to create the remotable class. A simple example is shown here, with a remotable class that returns a DataSet . This approach allows a remote client to retrieve database information without needing to connect directly to the server-side database. The remotable class gains the ability to be invoked remotely because it derives from MarshalByRefObject .

 using System; using System.Data; using System.Data.SqlClient; public class ProductsDB : MarshalByRefObject {     private static string connectionString = "Data Source=localhost;" +       "Initial Catalog=Northwind;Integrated Security=SSPI";     public DataTable GetProducts() {         string SQL = "SELECT * FROM Products";         // Create ADO.NET objects.         SqlConnection con = new SqlConnection(connectionString);         SqlCommand com = new SqlCommand(SQL, con);         SqlDataAdapter adapter = new SqlDataAdapter(com);         DataSet ds = new DataSet();         // Execute the command.         try {             con.Open();             adapter.Fill(ds, "Products");         } catch (Exception err) {             Console.WriteLine(err.ToString());         } finally {             con.Close();         }         return ds.Tables[0];     }     // This method allows you to verify that the object is running remotely.     public string GetHostLocation() {         return AppDomain.CurrentDomain.FriendlyName;     } } 

This class is defined in a class library assembly named RemoteObject.dll.

Note  

Ideally, the remote object won't retain any state. This characteristic allows you to use single-call activation , in which object instances are created at the beginning of each method call and released at the end, much like an XML Web service. This ensures that your objects consume the fewest possible server resources and saves you from the added complexity of implementing a lease policy to configure object lifetime.

Next you must create the component host ”the server-side application that hosts all instances of the remote class. You can use any type of long-running .NET Framework application for a component host (including Windows- based applications, Windows services, and Console applications). Here is the code for a simple console component host.

 using System; using System.Runtime.Remoting; public class Server {     private static void Main() {         // Register the remotable classes.         RemotingConfiguration.Configure("Server.exe.config");         // As long as this application is running, the remote objects         // will be accessible.         Console.WriteLine("Press a key to shut down the server.");         Console.ReadLine();     } } 

The component host uses a configuration file (app.config) to configure the classes it will support, the ports it will support for network communication, and the Uniform Resource Identifier (URI) that the client will use to access the object. Following is a simple configuration file that registers the RemoteObjects.RemoteObject class from the RemoteObject.dll assembly and provides network access through TCP/IP on the port 9080. This assembly must be in the global assembly cache (GAC) or in the same directory as the server application. The configuration file also configures the remote object to use single-call activation.

 <configuration>   <system.runtime.remoting>     <application>              <!-- Define the remotable object. -->       <service>          <wellknown              mode = "SingleCall"              type="RemoteObject.ProductsDB, RemoteObject"              objectUri="RemoteObject" />       </service>              <!-- Define the protocol used for network access.             You can use tcp or http channels. -->       <channels>         <channel ref="tcp" port="9080" />       </channels>              </application>   </system.runtime.remoting> </configuration> 

The component host never interacts with the remotable objects directly. All it does is register the appropriate types with the .NET Remoting infrastructure. After this point, clients can create object instances, and the server application can continue with other tasks . However, when the component host is closed, any remotable objects will be destroyed , and no more objects can be created.

The client application uses a similar configuration file that indicates the URL of the remote object and its type. The URL takes this form:

 [Protocol]://[Server]:[PortNumber]/[ObjectURI] 

Here is the complete client configuration file:

 <configuration>   <system.runtime.remoting>     <application>              <!-- Define the object this application will access remotely. -->       <client>         <wellknown type="RemoteObject.ProductsDB, RemoteObject"              url="tcp://localhost:9080/RemoteObject" />       </client>              <!-- Define the protocol used for network access.             The protocol must match the component host, but any port is valid.            A port of 0 means "dynamically choose an available port." -->       <channels>         <channel ref="tcp" port="0" />       </channels>            </application>   </system.runtime.remoting> </configuration> 

The client application uses the RemotingConfiguration.Configure method to register the objects it wants to call. Once this step is taken, the client can create the object exactly as it would create a local object. However, the object will actually be created in the component host application domain. You can verify this with the simple console client shown here:

 using System; using System.Runtime.Remoting; using System.Data; using RemoteObject; public class Client {     private static void Main() {         // Register the classes that will be accessed remotely.         RemotingConfiguration.Configure("Client.exe.config");         // (Now any attempts to instantiate the RemoteObjects.ProductsDB          // class will actually create a proxy to a remote instance.)         // Interact with the remote object through a proxy.         ProductsDB proxy = new ProductsDB();                  // Display the name of the component host application domain         // where the object executes.         Console.WriteLine("Object executing in: " + proxy.GetHostLocation());         // Get the DataSet and display its contents.         DataTable dt = proxy.GetProducts();                  foreach (DataRow row in dt.Rows) {             Console.WriteLine(row[1]);         }         Console.ReadLine();     } } 

To instantiate a remote object, the client needs to have a reference to the assembly where the class is defined. This presents an additional deployment step, which you can avoid by using an interface that defines the supported functionality.

Note  

To transmit data to and from a remote object, the types you use for parameters and return values must be serializable. All basic types (such as strings, numbers , and so on) are serializable. If you want to use custom classes to transmit data to or from a remote object, you must make sure these classes are also serializable using the Serializable attribute. (See recipe 16.1.)




C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net