MCAD/MCSD Training Guide (70-320): .NET Remoting

Date: Jun 27, 2003

Return to the article

Familiarize yourself with the remoting library of the .NET Framework and begin working toward creating a remotable class and hosting that class in several modes. You will also see the use of a configuration file to configure the remoting configuration for a remoting host and the remoting client.

Objectives

This chapter covers the following Microsoft-specified objective for the "Creating and Managing Microsoft Windows Services, Serviced Components, .NET Remoting Objects, and XML Web Services" section of the "Developing XML Web Services and Server Components with Microsoft Visual C# .NET and the Microsoft .NET Framework" exam:

Create and consume a .NET Remoting object.

Outline

Introduction

Application Boundaries

Distributed Applications

.NET Remoting Architecture

Applying .NET Remoting

Chapter Summary

Apply Your Knowledge

Study Strategies

Introduction

The .NET Framework provides a platform for building next-generation distributed applications. I start this chapter by introducing distributed application and how they are different from conventional applications. The .NET Framework allows you to create distributed applications in various ways. Two popular approaches are .NET remoting and ASP.NET Web services. I discuss .NET remoting in this chapter and cover ASP.NET Web services in the next two chapters.

In this chapter, you start learning about remoting by understanding its architecture first. You'll learn about various remoting elements such as the remotable class, remoting host, remoting client, channels, formatters, activation modes, and you'll also learn how these elements fit together to create a distributed application. I compare various choices available with each of the remoting elements and explain how to decide between those choices in a given scenario.

The next part of the chapter is code-intensive. You'll write code to practice creating small but fully functional distributed applications. While working with the Step-by-Step exercises you'll develop various skills instrumental for designing remoting applications and of course also for passing this exam.

I first show you how to create a class that can be remoted across the network and application boundaries. I then show how to create a remoting host that hosts the class so that the client program can take the services offered by the remotable class. I then show how to create a client program that can instantiate a remote object and invoke methods on it.

I discuss various types of applications that can work as remoting hosts, such as a console application, a Windows service, or IIS (Internet Information Services). I also discuss how you can use configuration files to conveniently modify the behavior of both the remoting host as well as the remoting client application.

Finally I show how to program the client application to invoke remote method calls asynchronously. Asynchronous method invocations, as you'll see, boost the responsiveness of the client application and keep users happy.

Application Boundaries

An application boundary defines the scope of an application. It encompasses various resources critical to an application's execution, such as address space, executable code, and the data used by the application. A typical multiprogramming execution environment such as Windows uses application boundaries to protect one application from affecting the execution of another application.

In this section, you'll learn about application boundaries with respect to Windows and the .NET Framework. You'll understand how application boundaries protect applications from poorly designed or faulty code. You'll also learn how application boundaries make it difficult to design applications that want to communicate beyond the application boundaries.

Process Boundary

A process is an application under execution. Windows isolates processes from each other to ensure that code running in one process cannot adversely affect other processes. Windows achieves this isolation by creating a process boundary. A process boundary ensures that

A process boundary allows processes to co-exist. However, it takes a lot of system resources to create, monitor, and terminate a process. In addition, when the processor switches between the processes, the processor must save and reset the execution context of the processes. Often an application involves several short-lived processes, which requires the system to spend a lot of resources just for process management.

Application Domain Boundary

The Common Language Runtime (CLR) provides a managed execution environment for .NET applications. The managed execution environment provides various services to the executing code, including cross-language integration, code access security, object lifetime management, and debugging and profiling support. The code executed by the CLR therefore is known also as managed code.

Unlike Windows, the CLR can verify type-safety of the programs to guarantee that a program does not request resources outside its own boundary. This characteristic of the CLR helps provide isolation between running programs at a lower cost than a process boundary incurs.

Instead of a process, the basic unit of isolation for running applications in the CLR is an application domain. An application domain (represented by System.AppDomain class) is the smallest executionunit for a .NET application. The CLR allows several application domains to run within a single Windows process and still provides the same level of isolation between applications as a Windows process does.

The application domains achieve isolation through application domain boundaries, which ensure that

It is much cheaper to create, monitor, and maintain an application domain than a process. In addition, the capability of an application domain to run multiple applications within the same process consumes less overhead in process switching; therefore application domains increase application performance.

You can create an application domain in a program by using the AppDomain class of System namespace. However, in most cases the application domains created and managed by the runtime hosts execute your code. Runtime hosts provide the environment to run managed code on behalf of the user. When you install the .NET Framework, you get three runtime hosts already configured—the Windows shell, ASP.NET, and Internet Explorer.

Distributed Applications

As described in the previous sections, processes as well as application domains provide a close, protected environment. As a result, objects in a process or an application domain cannot talk directly to objects in another process or another application domain.

However, in the increasingly connected world, enterprises and users demand distributed applications. Distributed applications allow objects to talk across process boundaries. Often, distributed applications also meet the following objectives:

Evolution of Distributed Applications

A well-designed distributed application has the potential to be more connected, more available, more scalable, and more robust than an application where all components run on a single computer. This is a desirable model for an enterprise application.

Traditionally, there have been several efforts to design frameworks for developing distributed applications. A few well-known frameworks are Distributed Computing Environment/Remote Procedure Calls (DEC/RPC), Microsoft Distributed Component Object Model (DCOM), Common Object Request Broker Architecture (CORBA), and Java Remote Method Invocation (RMI). Some of these implementations are widely deployed in enterprises.

However, modern business requirements are different from those of earlier days. Today, businesses seek solutions that can be developed rapidly, that integrate well with their legacy applications, and that interoperate well with their business partners. Each of the technologies already mentioned failed to satisfy one or more of these requirements.

In 2000, Microsoft introduced the .NET Framework for designing next-generation distributed applications. As you'll explore more in this book, the .NET Framework is specifically targeted to meet the needs of modern business, whether the need is rapid development or integration or interoperability.

Using the .NET Framework to Develop Distributed Applications

The .NET Framework provides various mechanisms to support distributed application development. Most of this functionality is present in the following three namespaces of the Framework Class Library (FCL):

Both .NET remoting and ASP.NET Web services provide a complete framework for designing distributed applications. Most programmers will use either .NET remoting or ASP.NET Web services rather than build a distributed programming framework from scratch with the System.Net namespace classes.

The functionality offered by .NET remoting and ASP.NET Web services appears very similar. In fact, ASP.NET Web services are actually built on the .NET remoting infrastructure. It is also possible to use .NET remoting to design Web services. Given the amount of similarity, how do you choose one over the other in your project? Simply put, the decision depends on the type of application you want to create. You'll use

You will learn more about architectural differences and specific features of both the technologies as you progress through this book. I will discuss .NET remoting in this chapter and will discuss ASP.NET Web services in Chapter 4, "Basic Web Services," and Chapter 5, "Advanced Web Services."

REVIEW BREAK

.NET Remoting Architecture

.NET remoting enables objects in different application domains to talk to each other. The real strength of remoting is in enabling the communication between objects when their application domains are separated across the network. In this case, remoting transparently handles details related to network communication.

Before I get into details, I'll first answer a basic question: How come remoting is able to establish cross-application domain communication when the application domains do not allow direct calls across their boundaries?

Remoting takes an indirect approach to application domain communication by creating proxy objects as shown in Figure 3.1. Both application domains communicate with each other by following these steps:

Figure 3.1 In this simplified view of .NET remoting, you can see that client and server communicate indirectly through a proxy object.

  1. When a client object requests an instance of the server object, the remoting system at the client side instead creates a proxy of the server object. The proxy object lives at the client but behaves just like the remote object; this leaves the client with the impression that the server object is in the client's process.

  2. When the client object calls a method on the server object, the proxy passes the call information to the remoting system on the client. This remoting system in turn sends the call over the channel to the remoting system on the server.

  3. The remoting system on the server receives the call information and, on the basis of it, invokes the method on the actual object on the server (creating the object if necessary).

  4. The remoting system on the server collects the result of the method invocation and passes it through the channel to the remoting system on the client.

  5. The remoting system at the client receives the server's response and returns the results to the client object through the proxy.

The process of packaging and sending method calls among objects, across application boundaries, via serialization and deserialization, as shown in the preceding steps, is also known as marshaling.

Now that you have a basic idea of how .NET remoting works, it's time to get into the details. In the next few sections, I'll explain various key components and terminology of .NET remoting.

Object Marshaling

Remotable objects are the objects that can be marshaled across the application domains. In contrast, all other objects are known as non-remotable objects. There are two types of remotable objects:

Marshal-by-value Objects

MBV objects reside on the server. When a client invokes a method on the MBV object, the MBV object is serialized, transferred over the network, and restored on the client as an exact copy of the server-side object. Now, the MBV object is locally available and therefore any method calls to the object do not require any proxy object or marshaling.

The MBV objects can provide faster performance by reducing the network roundtrips, but in the case of large objects, the time taken to transfer the serialized object from server to the client can be very significant. Furthermore, the MBV objects do not provide the privilege of running the remote object in the server environment.

You can create an MBV object by declaring a class with the Serializable attribute. For example:

//define a MBV remoting object
[Serializable()]
public class MyMBVObject
{
  //... 
}

If a class needs to control its own serialization, it can do so by implementing the ISerializable interface as follows:

//define a MBV remoting object
[Serializable()]
public class MyMBVObject : ISerializable
{
  //...
  //Implement custom serialization here
  public void GetObjectData(
    SerializationInfo info, 
    StreamingContext context)
  {
    //...
  }
  //...
}

NOTE

The NonSerialized Attribute By default public and private fields of a class are serialized, if a Serializable attribute is applied to the class. If you do not want to serialize a specific field in a serializable class, apply the NonSerialized attribute on that field. For example, in the class Sample, although field1 and field2 are serialized, field3 is not serialized because of the NonSerialized attribute.

[Serializable()]   
public class Sample 
{
  public int field1;
  public string field2;
  // A field that is not serialized.
  [NonSerialized()] 
  public string field3;
.
.
.
}

Marshal-by-reference Objects

The MBR objects are remote objects. They always reside on the server and all methods invoked on these objects are executed at the server side. The client communicates with the MBR object on the server by using a local proxy object that holds the reference to the MBR object.

Although the use of MBR objects increases the number of network roundtrips, they are a likely choice when the objects are prohibitively large or when the functionality of the object is available only in the server environment on which it is created.

You can create an MBR object by deriving the MBR class from the System.MarshalByRefObject class. For example:

//define a MBR remoting object
public class MyMBRObject : MarshalByRefObject
{
  //... 
}

Channels

Create and consume a .NET remoting object

Channels are the objects that transport messages across remoting boundaries such as application domains, processes, and computers. When a client calls a method on the remote objects, the details of the method call—such as parameters and so on—are transported to the remote object through a channel. Any results returned from the remote object are communicated back to the client again through the same channel.

The .NET remoting framework ensures that before a remote object can be called, it has registered at least one channel with the remoting system on the server. Similarly, the client object should specify a channel before it can communicate with a remote object. If the remote object offers more than one channel, the client can connect by using the channel that best suits its requirements.

A channel has two end points. The channel object at the receiving end of a channel (the server) listens to a particular protocol through the specified port number, whereas the channel object at the sending end of the channel (the client) sends information to the receiving end by using the protocol and port number specified by the channel object on the receiving end.

TIP

Port Numbers Should Be Unique on a Machine Each channel is uniquely associated with a TCP/IP port number. Ports are machine-wide resources; therefore, you cannot register a channel that listens on a port number that is already in use by some other channel on the same machine.

To participate in the .NET remoting framework, the channel object at the receiving end must implement the IChannelReceiver interface, whereas the channel object at the sending end must implement the IChannelSender interface.

The .NET Framework provides implementations for HTTP (Hypertext Transmission Protocol) and TCP (Transmission Control Protocol) channels. If you want to use a different protocol, you can define your own channel by implementing the IChannelReceiver and IChannelSender interfaces.

HTTP Channels

The HTTP channels use HTTP for establishing communication between the two ends. These channels are implemented through the classes of the System.Runtime.Remoting.Channels.Http namespace, as shown in Table 3.1.

Table 3.1: The HTTP Channel Classes

Class

Implements

Purpose

HttpServerChannel

IChannelReceiver

An implementation for a server channel that uses the HTTP to receive messages.

HttpClientChannel

IChannelSender

An implementation for a client channel that uses the HTTP to send messages.

HttpChannel

IChannelReceiver and IChannelSender

An implementation of a combined channel that provides the functionality of both the HttpServerChannel and the HttpClientChannel classes.


The following code example shows how to register a sender-receiver HTTP channel on port 1234:

using System; 
using System.Runtime.Remoting.Channels; 
using System.Runtime.Remoting.Channels.Http; 
//... 
 HttpChannel channel = new HttpChannel(1234); 
 ChannelServices.RegisterChannel(channel); 
//...

The ChannelServices class used in the code provides various remoting-related services. One of its static methods is RegisterChannel(), which helps in registering a channel with the remoting framework.

TCP Channels

The TCP channel uses TCP for establishing communication between the two ends. The TCP channel is implemented through various classes of the System.Runtime.Remoting.Channels.Tcp namespace, as shown in Table 3.2.

Table 3.2: The TCP Channel Classes

Class

Implements

Purpose

TcpServerChannel

IChannelReceiver

An implementation for a server channel that uses the TCP to receive messages.

TcpClientChanel

IChannelSender

An implementation for a client channel that uses the TCP to send messages.

TcpChannel

IChannelReceiver and IChannelSender

An implementation of a combined channel that provides the functionality for both TcpServerChannel and TcpClientChannel classes.


The following code example shows how to register a sender-receiver TCP channel on port 1234:

using System; 
using System.Runtime.Remoting.Channels; 
using System.Runtime.Remoting.Channels.Tcp; 
//... 
 TcpChannel channel = new TcpChannel(1234); 
 ChannelServices.RegisterChannel(channel); 
//...

Choosing Between the HTTP and the TCP Channels

Table 3.3 helps you make a decision about which channel to use in a given scenario. In summary, you'll normally use the TCP channel within a low-risk intranet. For more wide-reach applications, using the HTTP channel makes more sense unless the application's efficiency requirements justify the cost of creating a customized security system.

TIP

Support for Security .NET remoting has no built-in support for security. It instead depends on the remoting hosts to provide security. The only built-in remoting host that provides security for remote objects is IIS. Therefore, any secured objects must be hosted in IIS.

Table 3.3: Choosing Between the HTTP Channel and the TCP Channel

Channel

Scope

Efficiency

Security

HttpChannel

Wide, using the HTTP channel enables you to host the objects on a robust HTTP server such as IIS. HTTP channels can be used over the Internet, because firewalls do not generally block HTTP communication.

Less, because HTTP is a bulky protocol and has lots of extra overhead.

More, because when remote objects are hosted in IIS, the HttpChannel can immediately take advantage of Secure Sockets Layer (SSL), Integrated Windows Authentication or Kerberos.

TcpChannel

Narrow, using the TCP channel over the Internet would require opening certain ports in the firewall and could lead to security breaches.

More, because TCP uses raw sockets to transmit data across the network.

Less, until you implement a custom security system using the classes provided in the System.Security namespace.


Formatters

Create and consume a .NET Remoting object

Formatters are the objects that are used to encode and serialize data into messages before they are transmitted over a channel. At the other end of the channel, when the messages are received, formatters decode and deserialize the messages.

To participate in the .NET remoting framework, the formatter classes must implement the IFormatter interface. The .NET Framework packages two formatter classes for common scenarios: the BinaryFormatter class and the SoapFormatter class. If you want to use a different formatter, you can define your own formatter class by implementing the IFormatter interface.

The SOAP Formatter

SOAP (Simple Object Access Protocol) is a simple, XML-based protocol for exchanging types and messages between applications. SOAP is an extensible and modular protocol; it is not bound to a particular transport mechanism such as HTTP or TCP.

The SOAP formatter is implemented in the SoapFormatter class of the System.Runtime.Serialization.Formatters.Soap namespace.

SOAP formatting is an ideal way of communicating between applications that use non-compatible architectures. However, SOAP is very verbose. SOAP messages require more bytes to represent the data as compared to the binary format.

The Binary Formatter

Unlike SOAP, the binary format used by the .NET Framework is proprietary and can be understood only within .NET applications. However, as compared to SOAP, the binary format of representing messages is very compact and efficient.

The binary formatter is implemented in the BinaryFormatter class of the System.Runtime.Serialization.Formatters.Binary namespace.

Channels and Formatters

The HTTP channel uses the SOAP formatter as its default formatter to transport messages to and from the remote objects. The HTTP channel uses SoapClientFormatterSinkProvider and SoapServerFormatterSinkProvider classes to serialize and deserialize messages through the SoapFormatter class. You can create industry-standard XML Web services when using SOAP formatter with the HTTP channel.

The TCP channel uses the binary format by default to transport messages to and from the remote object. The TCP channel uses BinaryClientFormatterSinkProvider and BinaryServerFormatterSinkProvider classes to serialize and deserialize messages through the BinaryFormatter class.

However, channels are configurable. You can configure the HTTP channel to use the binary formatter or a custom formatter rather than the SOAP formatter. Similarly, the TCP channel can be configured to use the SOAP formatter or a custom formatter rather than the binary formatter.

Figure 3.2 compares the various combinations of channels and formatters on the scale of efficiency and compatibility. You can use this information to decide which combination of channel and formatter you would chose in a given scenario.

Figure 3.2 A TCP channel with a binary formatter provides maximum efficiency, whereas an HTTP channel with a SOAP formatter provides maximum interoperability.

REVIEW BREAK

Remote Object Activation

Between the two types of objects you have studied—the MBV objects and the MBR objects—only MBR objects can be activated remotely. No remote activation is needed in the case of MBV objects because the MBV object itself is transferred to the client side.

TIP

Remotable Members An MBR object can remote the following types of members:

Non-static public methods

Non-static public properties

Non-static public fields

Based on the activation mode, MBR objects are classified in the following two categories:

Server-Activated Objects

Server-activated objects (SAOs) are those remote objects whose lifetime is directly controlled by the server.

When a client requests an instance of a server-activated object, a proxy to the remote object is created in the client's application domain. The remote object is only instantiated (or activated) on the server when the client calls a method on the proxy object.

The server-activated objects provide limited flexibility because only their default (parameter-less) constructors can be used to instantiate them.

There are two possible activation modes for a server-activated object:

NOTE

Well-Known Objects Remote objects activated in SingleCall or Singleton activation mode are also known as server-activated objects or well-known objects.

SingleCall Activation Mode

In the SingleCall activation mode, an object is instantiated for the sole purpose of responding to just one client request. After the request is fulfilled, the .NET remoting framework deletes the object and reclaims its memory.

Objects activated in the SingleCall mode are also known as stateless because the objects are created and destroyed with each client request and therefore do not maintain state across the requests. This behavior of the SingleCall mode accounts for greater server scalability as an object consumes server resources for only a small period, therefore allowing the server to allocate resources to other objects.

TIP

Load-Balancing and SingleCall Activation Sometimes to improve the overall efficiency of an application, the application may be hosted on multiple servers that share the incoming requests to it. In this case, a request can go to any of the available servers for processing. This scenario is called a load-balancing environment.

Because the SingleCall objects are stateless, it does not matter which server processes their requests. For this reason, SingleCall activation is ideally suited for load- balanced environments.

The SingleCall activation mode is a desired solution when

Scenarios that are often well suited for the SingleCall activation mode are those applications where the object is required by the client to do a small amount of work and then the object is no longer required. Some common examples include retrieving the inventory level for an item, displaying tracking information for a shipment, and so on.

Singleton Activation Mode

In the Singleton activation mode, there is at most one instance of the remote object, regardless of the number of clients accessing it.

A Singleton mode object can maintain state information across the method calls. Therefore, they are also sometimes known as stateful objects. The state maintained by the Singleton mode server-activated object is globally shared by all its clients.

A Singleton object does not exist on the server forever. Its lifetime is determined by the lifetime lease of the object. I'll discuss lifetime leases shortly in the section, "Lifetime Leases."

A Singleton object is a desired solution when

Singleton activation mode is useful in scenarios such as in a chat server where multiple clients talk to the same remote object and share data between one another.

Client-Activated Objects

Client-activated objects (CAOs) are those remote objects whose lifetime is directly controlled by the client. This is in direct contrast with SAOs, where the server, not the client, has complete control over objects' lifetimes.

Client-activated objects are instantiated on the server as soon as the client requests the object to be created. Unlike SAOs, CAOs do not delay object creation until the first method is called on the object.

You can use any of the available constructors of the remotable class to create a CAO. A typical CAO activation involves the following steps:

  1. When the client attempts to create an instance of the server object, an activation request message is sent to the remote server.

  2. The server then creates an instance of the requested class by using the specified constructor and returns an ObjRef object to the client application that invoked it. The ObjRef object contains all the required information to generate a proxy object that is capable of communicating with a remote object.

  3. The client uses the ObjRef object to create a proxy of the server object on the client side.

An instance of the CAO serves only the client that was responsible for its creation, and the CAO doesn't get discarded with each request. For this reason, a CAO can maintain state with each client that it is serving, but unlike Singleton SAOs, different CAOs cannot share a common state.

The lifetime of a CAO is determined by the lifetime leases. I'll talk more about this topic shortly in a section titled "Lifetime Leases."

A CAO is a desired solution when

CAOs are useful in scenarios such as entering a complex purchase order where multiple roundtrips are involved and clients want to maintain their own private state with the remote object.

Comparing the Object Activation Techniques

Based on the discussions in the previous section, the various object activation techniques can be compared as shown in Figure 3.3.

Figure 3.3 The SingleCall server activation offers maximum scalability, whereas the client activation offers maximum flexibility.

SingleCall server activation mode offers maximum scalability because the remote object occupies server resources for the minimum length of the time. This enables the server to allocate its resources between many clients.

On the other hand, the client activation of remote objects offers maximum flexibility because you have complete control over the construction and lifetime of the remote object.

Lifetime Leases

A lifetime lease is the period of time that a particular object shall be active in memory before the .NET framework deletes it and reclaims its memory. Both Singleton SAOs and CAOs use lifetime leases to determine how long they should continue to exist.

NOTE

Leases and Activation Mode Leases apply only to Singleton SAOs and CAOs. With SingleCall SAOs, objects are created and destroyed with each method call.

TIP

Custom and Infinite Lifetimes A remote object can choose to have a custom defined lifetime if the InitializeLifetimeService() method of the base class, MarshalByRefObject, is overridden. If the InitializeLifetimeService() method returns a null, the type tells the .NET remoting system that its instances are intended to have an infinite lifetime.

A lifetime lease is represented by an object that implements the ILease interface that is defined in the System.Runtime.Remoting.Lifetime namespace. Some of the important members of this interface are listed in Table 3.4.

Table 3.4: Important Members of the ILease Interface

Member Name

Type

Description

CurrentLeaseTime

Property

Gets the amount of time remaining on the lease before the object is marked for garbage collection.

InitialLeaseTime

Property

Gets or sets the initial time for the lease. If the object does not receive any method calls, it lives for only this period.

Register()

Method

Registers a sponsor for the lease.

Renew()

Method

Renews a lease for the specified time.

RenewOnCallTime

Property

Gets or sets the amount of time by which a call to the remote object will increase the CurrentLeaseTime.

SponsorshipTimeout

Property

Gets or sets the amount of time to wait for a sponsor to return with a lease renewal time.


Simply speaking, the lease works as follows:

Sponsors are the objects responsible for dynamically renewing an object's lease if its lease expires. Sponsors implement the ISponsor interface and are registered with the lease manager by calling the ILease.Register() method. When the lease for such an object expires, the lease manager calls the ISponsor.Renewal() method implemented by the sponsor objects to renew the lease time. For more information about sponsors, refer to the "Renewing Leases" topic in the .NET Framework Developer's Guide.

REVIEW BREAK

Applying .NET Remoting

So far, I have discussed the architecture and various concepts related to .NET remoting. In this section, you will learn how to apply these concepts to see remoting in action. In particular, you will learn to

Creating a Remotable Class

Creating a remotable class is simple. All you need to do is to inherit a class from the MarshalByRefObject class or any of its derived classes. Step-by-Step 3.1 creates a remotable class named DbConnect. This class connects to a specified SQL Server database and enables you to execute a SELECT SQL statement by using its ExecuteQuery() method.

STEP BY STEP 3.1 - Creating a Remotable Class

  1. Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 320C03. Click OK.

  2. Add a new Visual C# .NET class library named StepByStep3_1 to the solution.

  3. In the Solution Explorer, rename the default Class1.cs to DbConnect.cs.

  4. Open the DbConnect.cs and replace the code with the following code:
    using System;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace StepByStep3_1
    {
      // Marshal-by-Reference Remotable Object
      public class DbConnect : MarshalByRefObject
      {
        private SqlConnection sqlconn;
    
        // Default constructor connects to the
        // Northwind database by calling
        // the overloaded constructor
        public DbConnect() : this("Northwind")
        {
        }
    
        // Parameterized constructor connects to the
        // specified database
        public DbConnect(string DbName)
        {
          // Open a connection to the specified
          // sample SQL Server database
          sqlconn = new SqlConnection(
            @"data source=(local);" +
            @"initial catalog=" + DbName +
            @";integrated security=SSPI");
          Console.WriteLine(
            "Created a new connection " +
            "to the {0} database.", DbName);
        }
    
        public DataSet ExecuteQuery(string strQuery)
        {
          Console.Write("Starting to execute " +
            "the query...");
          // Create a SqlCommand object
          // to represent the query
          SqlCommand sqlcmd =
            sqlconn.CreateCommand();
          sqlcmd.CommandType = CommandType.Text;
          sqlcmd.CommandText = strQuery;
    
          // Create a SqlDataAdapter object
          // to talk to the database
          SqlDataAdapter sqlda =
            new SqlDataAdapter();
          sqlda.SelectCommand = sqlcmd;
          // Create a DataSet to hold the results
          DataSet ds = new DataSet();
          try
          {
            // Fill the DataSet
            sqlda.Fill(ds, "Results");
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message,
              "Error executing query");
          }
          Console.WriteLine("Done.");
          return ds;
        }
      }
    }
  5. Select Build, Build StepByStep3_1. This step packages the remotable class into the file StepByStep3_1.dll, which is located in the bin\Debug or bin\Release directory of your project. You can navigate to it through the Solution Explorer: Just select the project and click the Show All Files button in the Solution Explorer toolbar.

Although you now have a remotable class available to you, it cannot be called directly from the client application domains yet. For a remotable class to be activated, you need to connect the class to the remoting framework. You'll learn how to do that in the next section.

Creating a Server-Activated Object

Create and consume a .NET Remoting object.

A remotable class is usually connected with the remoting framework through a separate server program. The server program listens to the client request on a specified channel and instantiates the remote object or invokes calls on it as required.

It is a good idea to keep the remotable class and server program separate; this allows the design to be modular and the code to be reusable.

In this section, I'll show you how to create a remoting server. To achieve this, the remoting server must take the following steps:

  1. Create a server channel that listens on a particular port to the incoming object activation requests from other application domains. The following code segment shows how to create a TCP server channel and an HTTP server channel:
    // Register a TCP server channel on port 1234
    TcpServerChannel channel = new TcpServerChannel(1234);
    
    // Register an HTTP server channel on port 1235
    HttpServerChannel channel = new HttpServerChannel(1235);
  2. Register the channel with the remoting framework. This registration is performed through the RegisterChannel() method of the ChannelServices class:
    // Register the channel with the remoting framework
    ChannelServices.RegisterChannel(channel); 
  3. Register the remotable class with the remoting framework. For a server-activated object, the RegisterWellKnownServiceType() method of the RemotingConfiguration class is used to perform this registration, as follows:
    //Register a remote object with the remoting framework
    RemotingConfiguration.RegisterWellKnownServiceType(
      typeof(DbConnect), // type of the remotable class
      "DbConnect",   // URI of the remotable class
      WellKnownObjectMode.SingleCall //Activation mode
    );
  4. Here, the first parameter is the type of the remotable class. The second parameter specifies the uniform resource identifier (URI) through which the server publishes the remote object's location. The last parameter specifies the activation mode. The activation mode should be one of the two possible values of the WellKnownObjectMode enumeration—SingleCall and Singleton.

TIP

Accessing an Object Through Multiple Channels From step 2 and step 3, note that the channel registration and the remote object registration are not related. In fact, a remote object can be accessed through all registered channels.

As I discussed, earlier, an SAO can be activated in two different modes—SingleCall and Singleton. In the next few sections, I'll cover how to create a remoting server for activating objects in each of these modes. I'll also tell the story on the other side of the channel, that is, how to connect the client program to the remoting framework so that it can instantiate the SAO and call methods on it.

Using the SingleCall Activation Mode to Register a Remotable Class As a Server-Activated Object

In this section, I'll demonstrate how to create a server that exposes the remotable class through the remoting framework. The server process here is a long-running user interface-less process that continues to listen to incoming client requests on a channel.

Ideally, you should write this type of server program as a Windows service or use an existing Windows service such as Internet Information services (IIS) to work as the remoting server. But I have chosen to write the server program as a console application mainly because I'll use the console window to display various messages that will help you understand the workings of the remoting system.

However, later in this chapter, in the section "Using IIS As an Activation Agent," I cover how to use IIS as a remoting server. I talk about Windows services later in Chapter 6, "Windows Services."

STEP BY STEP 3.2: Using the SingleCall Activation Mode to Register a Server-Activated Object

  1. Add a new Visual C# .NET console application named StepByStep3_2 to the solution.

  2. In the Solution Explorer, right-click project StepByStep3_2 and select Add Reference from the context menu. In the Add Reference dialog box, select the .NET tab, select the System.Runtime.Remoting component from the list view, and click the Select button. Now select the Projects tab, select the Project named StepByStep3_1 (contains the remotable object) from the list view, and click the Select button. Both the selected projects then appear in the Selected Components list, as shown in Figure 3.4. Click OK.

Figure 3.4 The Add Reference dialog box enables you to add references to components.

  1. In the Solution Explorer, rename the default Class1.cs to DbConnectSingleCallServer.cs. Open the file and change the name of the class to DbConnectSingleCallServer in the class declaration.

  2. Add the following using directives:
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
  3. Add the following code in the Main() method:
    [STAThread]
    static void Main(string[] args)
    {
      // Create and register a TCP server channel
      // that listens on port 1234
      TcpServerChannel channel =
        new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      // Register the service that publishes
      // DbConnect for remote access in SingleCall mode
      RemotingConfiguration.RegisterWellKnownServiceType
        (typeof(StepByStep3_1.DbConnect), "DbConnect",
        WellKnownObjectMode.SingleCall);
    
      Console.WriteLine("Started server in the " +
        "SingleCall mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  4. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class for remote invocation by using the SingleCall activation mode.

Step-by-Step 3.2 uses a receiver TCP channel (TcpServerChannel) to register a remotable class with the remoting framework. However, converting this program to use the HTTP channel is not difficult—you just need to change all instances of Tcp to Http.

Step-by-Step 3.2 creates a remoting host that listens on port 1234. This is an arbitrary port number that may or may not work on your computer. A good idea is to check whether a port is already in use by some other application before running this program. You can do this from the command line by using the Windows netstat command.

This suggestion works only in a test scenario; it is not reasonable to instruct a customer to check whether the port is available before starting the application. If the application will run entirely on your company's network, you can safely use a port in the private port range of 49152 through 65535—provided, of course, that the port number you choose is not used by another internal application. If you are distributing the application, you should get a port number registered from IANA (Internet Assigned Numbers Authority). You can see a list of already assigned port numbers at http://www.iana.org/assignments/port-numbers.

Instantiating and Invoking a Server-Activated Object

At this stage, you have a remotable object as well as a remoting server ready. In this section, I'll show you how to create a remoting client and use it to send messages to the remoting server to activate the remote object. To achieve this, the remoting client needs to take the following steps:

  1. Create and register a client channel that is used by the remoting framework to send messages to the remoting server. The type of channel used by the client should be compatible with the channel used by the server. The following examples show how to create a TCP client channel and an HTTP client channel:

    // Create and register a TCP client channel
    TcpClientChannel channel = new TcpClientChannel();
    ChannelServices.RegisterChannel(channel);
    
    // Create and register an HTTP client channel
    HttpClientChannel channel = new HttpClientChannel();
    ChannelServices.RegisterChannel(channel);

TIP

Client Channel Registration You do not specify a port number when you register the client channel. The port number is instead specified at the time of registering the remote class in the client's application domain.

  1. Register the remotable class as a valid type in the client's application domain. The RegisterWellKnownClientType() method of the RemotingConfiguration class is used to perform this registration, as shown below:
    // Register the remote class as a valid
    // type in the client's application domain
    RemotingConfiguration.RegisterWellKnownClientType(
      // Remote class
      typeof(DbConnect),
      // URL of the remote class
      "tcp://localhost:1234/DbConnect"
    );
  2. Here, the first parameter is the type of the remotable class. The second parameter specifies the uniform resource identifier (URI) through which the server publishes the location of the remote object. Localhost maps to your local development machine. If the remote object is on some other computer, you'll replace localhost with the name of the computer.
  3. Instantiate the SAO on the server. You can only use the default constructor.
    // Instantiate the remote object
    DbConnect dbc = new DbConnect();

TIP

Instantiating a Server-Activated Object You can instantiate an SAO at client side only, by using its default constructor.

I demonstrate the preceding steps in Step-by-Step 3.3, where I create the client program as a Windows application that accepts a SQL statement from the user and passes it to the remotable object. The rows returned by the remotable object are displayed in a DataGrid control.

STEP BY STEP 3.3: Instantiating and Invoking a Server-Activated Object

  1. Add a new Visual C# .NET Windows application named StepByStep3_3 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_1;
  5. Place two GroupBox controls, a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Arrange the controls as shown in Figure 3.5.

Figure 3.5 This form invokes a method of a remote object to execute the given query.

  1. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  2. Double-click the form and add the following code in the Load event handler:

  3. private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Register a TCP client channel
      TcpClientChannel channel = new TcpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Register the remote class as a valid
      // type in the client's application domain
      RemotingConfiguration.RegisterWellKnownClientType(
        // Remote class
        typeof(DbConnect),
        // URL of the remote class
        "tcp://localhost:1234/DbConnect"
        );
    
      // Instantiate the remote class
      dbc = new DbConnect();
    }

NOTE

Starting Multiple Instances of a Program When you run a project from the Visual Studio .NET IDE, the default behavior is to run the program in debug mode. However, debug mode does not allow you to start another program from the IDE until you finish the current execution. This can be inconvenient if you want to start multiple client programs from within Visual Studio. A solution is to set the project you want to run first as the startup object and select Debug, Run Without Debugging to run the project. This doesn't lock Visual Studio .NET in the debug mode and you can run more programs from within IDE by using the same technique.

  1. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  2. Right-click on the name of the solution in the Solution Explorer window and select Properties. This opens the Solution '320C03' Property Pages dialog box. In the dialog box, select the Multiple Startup Projects check box and then select the action Start for StepByStep3_2 and StepByStep3_3 and set the action to None for other projects, as shown in Figure 3.6. Make sure that StepByStep3_2 is placed above StepByStep3_3, if it is not already in order, and then click on the Move Up and Move Down button to get the correct order.

Figure 3.6 Use the Solution Property Pages dialog box to control what projects are started and in what order.

  1. Build the project. Select Debug, Start to run the project. You should see a command window displaying a message that the server is started in the SingleCall mode.

  2. Shortly after, you'll see a Windows form for the client program. Enter a query in the text box and click the Execute Query button. The client invokes a method on the remote object and binds the results from the remote method to the DataGrid control, as shown in Figure 3.7.

Figure 3.7 The remoting client populates the data grid with the result returned from the method invocation on a remote object.

In the preceding steps, I have chosen to start the client and server programs within the Visual Studio .NET IDE. You can also start these programs by clicking on StepByStep3_2.exe and StepByStep3_3.exe, respectively, from the Windows explorer. Note that the server program should be running before you click on the Execute Query button on the client program.

At the end of Step-by-Step 3.3, when you look at the console window of the server program, note the output as shown in Figure 3.8. The remote object is created every time you click on the Execute Query button. That's normal behavior based on what you have read so far in the chapter; however, what's peculiar is that for the first call, the constructor is called twice.

Figure 3.8 The SingleCall remoting server creates a new instance of the remote object with each request.

In fact, calling the constructor twice for the first request on a server is also a regular behavior of SingleCall object activation. The following two points explain why this happens:

Some of you might have a question about why I included a reference to StepByStep3_1.dll in this project. You may support your argument by saying that the DbConnect class contained in the StepByStep3_1.dll is a remotable class and its proper place is on the server—not on the client.

Well said, but I have included the reference to StepByStep3_1.dll for the following reasons:

However, you would say this won't work in real life, because the StepByStep3_1.dll may contain important business logic that you do not want your customers to decompile.

You're right! And I have a solution for that in the form of interface assemblies. With interface assemblies, you just share the interface of an assembly with your customers, not the actual business logic. I'll show you how to create interface assemblies later in this chapter in the section "Using Interface Assemblies to Compile Remoting Clients."

Using the Singleton Activation Mode to Register a Remotable Class As a Server-Activated Object

You can activate the same remotable object in different modes without making any changes to the remotable object itself. In the case of SAOs, the choice of activation mode is totally with the server. In this section, I'll show you how to create a remoting server that publishes the DbConnect class as an SAO by using the Singleton activation mode. I'll use the same client program that was created in Step-by-Step 3.3 to test this Singleton server.

STEP BY STEP 3.4: Using the Singleton Activation Mode to Register a Server-Activated Object

  1. Add a new Visual C# .NET console application named StepByStep3_4 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnectSingletonServer.cs. Open the file and change the name of the class to DbConnectSingletonServer in the class declaration.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
  5. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Create and Register a TCP server channel
      // that listens on port 1234
      TcpServerChannel channel =
           new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      // Resgister the service that publishes
      // DbConnect for remote access in Singleton mode
      RemotingConfiguration.RegisterWellKnownServiceType
        (typeof(StepByStep3_1.DbConnect), "DbConnect",
         WellKnownObjectMode.Singleton);
      Console.WriteLine("Started server in the " +
        "Singleton mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  6. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class for remote invocation by using the Singleton activation mode.

  7. Set StepByStep3_4, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode.

  8. Now set StepByStep3_3, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created when the form loads. The code binds the results from the remote method to the DataGrid control.

Although for the client there has been no changes in the output, if you note the messages generated by the server (see Figure 3.9), you can see that just one instance of the connection is created and is shared by all the clients that connect to this server.

Figure 3.9 The Singleton remoting server uses the same instance of the remote object with subsequent requests.

Guided Practice Exercise 3.1

The objective of this exercise is to create a remoting server that exposes the DbConnect class of StepByStep3_1 as a Singleton SAO. However, the server and client should communicate via the HTTP channels and SOAP formatter. Other than this, the server and the client programs are similar to those created in Step-by-Step 3.4 and Step-by-Step 3.3, respectively.

How would you use the HTTP channel and SOAP formatter to establish communication between the remoting server and client?

This exercise helps you practice creating a remoting server and client that uses the HTTP channel and SOAP formatter for communication.

You should try working through this problem on your own first. If you get stuck, or if you'd like to see one possible solution, follow these steps:

  1. Add a new Visual C# .NET console application named GuidedPracticeExercise3_1_Server to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnectSingletonServer.cs. Open the file and change the name of the class to DbConnectSingletonServer in the class declaration.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Http;
  5. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Create and Register an HTTP server channel
      // that listens on port 1234
      HttpServerChannel channel =
        new HttpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      // Register the service that publishes
      // DbConnect for remote access in Singleton mode
      RemotingConfiguration.RegisterWellKnownServiceType
        (typeof(StepByStep3_1.DbConnect), "DbConnect",
        WellKnownObjectMode.Singleton);
      Console.WriteLine("Started server in the " +
        "Singleton mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  6. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class (the remotable object) for remote invocation by using the Singleton activation mode via the HTTP channel.

  7. Add a new Visual C# .NET Windows application named GuidedPracticeExercise3_1_Client to the solution.

  8. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  9. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  10. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Http;
    using StepByStep3_1;
  11. Place two GroupBox controls, a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute) and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of the form.

  12. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  13. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load
      (object sender, System.EventArgs e)
    {
      // Register an HTTP client channel
      HttpClientChannel channel =
        new HttpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Register the remote class as a valid
      // type in the client's application domain
      RemotingConfiguration.RegisterWellKnownClientType(
        // Remote class
        typeof(DbConnect),
        // URL of the remote class
        "http://localhost:1234/DbConnect"
        );
    
      // Instantiate the remote class
      dbc = new DbConnect();
    }
  14. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  15. Build the project. Set the GuidedPracticeExercise3_1_Server, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode. The remoting server is now ready to receive SOAP messages via HTTP.

  16. Now set GuidedPracticeExercise3_1_Client, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object and sends the messages in SOAP format via HTTP.

If you have difficulty following this exercise, review the sections "Channels," "Formatters," "Creating a Remotable Class," and "Creating a Server-Activated Object," earlier in this chapter. Make sure that you also perform Step-by-Step 3.1 to Step-by-Step 3.4. After doing that review, try this exercise again.

Creating a Client-Activated Object

Create and consume a .NET Remoting object.

When exposing a remotable class as a CAO, no changes are required on the remotable class. Instead, only the server and client differ on how the remotable class is registered with the remoting system.

In this section, I'll show you how to register a remotable class as a CAO and how to instantiate and invoke a CAO from a remoting client.

Registering a Remotable Class As a Client-Activated Object

You'll have to take the following steps to register a remotable class as a CAO on the server:

  1. Create a server channel that listens on a particular port to the incoming object activation requests from other application domains. The following examples show how to create a TCP server channel and an HTTP server channel:

    // Register a TCP server channel on port 1234
    TcpServerChannel channel = new TcpServerChannel(1234);
    
    // Register an HTTP server channel on port 1235
    HttpServerChannel channel =
      new HttpServerChannel(1235);
  2. Register the channel with the remoting framework. This registration is performed through the RegisterChannel() method of the ChannelServices class:

    // Register the channel with the remoting framework
    ChannelServices.RegisterChannel(channel); 
  3. Register the remotable class with the remoting framework. For a client-activated object, use the RegisterActivatedServiceType() method of the RemotingConfiguration class to perform the registration, as shown below:

    // Register a remote object as a CAO
    // with the remoting framework
    RemotingConfiguration.RegisterActivatedServiceType(
      typeof(DbConnect)); // type of the remotable class
  4. Here, the only parameter is the type of the remotable class.

Step-by-Step 3.5 shows how to expose the familiar DbConnect class from StepByStep3_1 as a CAO.

STEP BY STEP 3.5: Registering a Remotable Class As a Client-Activated Object

  1. Add a new Visual C# .NET console application named StepByStep3_5 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnectCAOServer.cs. Open the file and change the name of the class to DbConnectCAOServer in the class declaration.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_1;
  5. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Create and register a TCP channel on port 1234
      TcpServerChannel channel =
        new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      // Register the client-activated object
      RemotingConfiguration.RegisterActivatedServiceType
        (typeof(DbConnect));
      Console.WriteLine(
       "Started server in the Client Activation mode");
      Console.WriteLine(
        "Press <ENTER> to terminate server...");
      Console.ReadLine();
    } 
  6. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect (the remotable object) class for remote invocation by using the client activation mode.

Instantiating and Invoking a Client-Activated Object

To instantiate and invoke a client-activated object, the remoting client needs to take the following steps:

  1. Create and register a client channel that the remoting framework uses to send messages to the remoting server. The type of the channel the client uses should be compatible with the channel the server uses. The following examples show how to create a TCP client channel and an HTTP client channel:

    // Create and register a TCP client channel
    TcpClientChannel channel = new TcpClientChannel();
    ChannelServices.RegisterChannel(channel);
    
    // Create and register an HTTP client channel
    HttpClientChannel channel = new HttpClientChannel();
    ChannelServices.RegisterChannel(channel);
  2. Register the remotable class as a valid type in the client's application domain. This registration is performed using the RegisterActivatedClientType() method of the RemotingConfiguration class as shown below:

    // Register DbConnect as a type on client,
    // which can be activated on the server
    RemotingConfiguration.RegisterActivatedClientType
       (typeof(DbConnect), "tcp://localhost:1234");

    Here, the first parameter is the type of the remotable class. The second parameter specifies the uniform resource identifier (URI) through which the server publishes the location of the remote object.

TIP

Instantiating a Client-Activated Object You can instantiate a CAO at the client side by using any of its available constructors.

  1. Instantiate the CAO on the server by using the desired constructor.

    // Instantiate the remote object
    DbConnect dbc = new DbConnect("Pubs");

I'll demonstrate the preceding steps in Step-by-Step 3.6. This step is similar to the client program created in Step-by-Step 3.3, but this time the client allows the user to choose between the databases on the server.

STEP BY STEP 3.6: Instantiating and Invoking a Client-Activated Object

  1. Add a new Visual C# .NET Windows application named StepByStep3_6 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_1;
  5. Place three GroupBox controls (grpDatabases, grpQuery and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery, with its MultiLine property set to true), two Button controls (btnSelect and btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.10 for this form's design.

  6. Select the Items property of the cboDatabases control in the Properties window and click on the (...) button. This opens the String Collection Editor dialog box. Enter the following database names in the editor:

    Northwind
    Pubs
    GrocerToGo 
  7. Click OK to add the databases to the Items collection of the cboDatabases control.

  8. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  9. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      cboDatabases.SelectedIndex = 0;
      grpQuery.Enabled = false;
    }
  10. Double-click the btnSelect control and add the following code in the Click event handler:

    private void btnSelect_Click(
      object sender, System.EventArgs e)
    {
      // Disable the Databases group box and
      // enable the Query group box
      grpDatabases.Enabled = false;
      grpQuery.Enabled = true;
    
      // Register a TCP client channel
      TcpClientChannel channel = new TcpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Register the remote class as a valid
      // type in the client's application domain
      // by passing the Remote class and its URL
      RemotingConfiguration.RegisterActivatedClientType
        (typeof(DbConnect), "tcp://localhost:1234");
    
      // Instantiate the remote class
      dbc = new DbConnect(
        cboDatabases.SelectedItem.ToString());
    }
  11. Double-click the btnExecute control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  12. Build the project. You now have a remoting client ready to use.

  13. Set StepByStep3_5, the CAO remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the client activation mode.

  14. Now set StepByStep3_6, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Select a database from the combo box and click the Select button. An instance of the remotable object, DbConnect, is created with the selected database. Now enter a query in the text box and click the button. The code invokes a method on the remote object. The code binds the results from the remote method to the DataGrid control as shown in Figure 3.10.

Figure 3.10 A CAO client allows database selection by taking advantage of the capability to call various constructors.

  1. Again select Debug, Start Without Debugging to run one more instance of the remoting client. Select a different database from the combo box and click the Select button. An instance of the remotable object, DbConnect, is created with the selected database. Now enter a query in the text box and click the button. You should see that the second instance of the client fetches the data from the selected database. Switch to the server command window. You can see that the remote object is instantiated twice with different databases, as shown in Figure 3.11. This shows that the client activation creates an instance of a remotable object for each client.

Figure 3.11 The client-activated remoting server creates a new instance of a remote object for each client.

REVIEW BREAK

Using Configuration Files to Configure the Remoting Framework

Create and consume a .NET Remoting object: Create client configuration files and server configuration files.

In all the examples written so far, I have written code to register the channel and remote object with the remoting framework. This approach of specifying settings is also known as programmatic configuration. Although this approach works fine, there is a drawback: Every time you decide to make any changes in how the channel and the remote objects are registered, you have to recompile the code to see the effect of the changes.

Alternatively, you can store the remoting settings in an XML-based configuration file instead of the code file. The program can automatically pick up any changes made to the configuration file when it executes the next time. You need not recompile the sources. This approach of specifying configuration settings is also known as declarative configuration.

The declarative configuration can be specified at two levels:

NOTE

Use Naming Conventions for the Application-Level Configuration Files Although it is not required, you should prefer using the .NET Framework naming convention for naming the application-level configuration file. This ensures that configuration settings for an application, whether remoting-related or security-related, are all in one place.

TIP

Application-Level Configuration File Takes Precedence over Machine-Level Configuration When you specify both application-level configuration as well as machine-level configuration for an application, the application-level configuration takes priority over the machine-level configuration.

The general format of the configuration file is as follows:

 <configuration>
 <system.runtime.remoting>
 <application>
  <lifetime>
   <!-- Use this section to specify the -->
   <!-- lifetime information for all  -->
   <!-- the objects in the application. -->
  </lifetime>
  <service>
   <!-- Use this section to specify how a remote-->
   <!-- object is exposed by the remoting server-->
   <!-- Use the <wellknown> tag to configure-->
   <!-- an SAO and use the <activated> tag   -->
   <!-- to configure a CAO           -->
   <wellknown />
   <activated />
  </service>
  <client>
   <!-- Use this section to specify how a remote-->
   <!-- object is consumed by the client    -->
   <!-- Use the <wellknown> tag to configure a -->
   <!-- call to the SAO and use the <activated> -->
   <!-- tag to configure a call to the CAO   -->
   <wellknown />
   <activated />
  </client>
  <channels>
   <!-- Use this section to configure the    -->
   <!-- channels that the application uses   -->
   <!-- to communicate with the remote objects -->
  </channels>
 </application>
</system.runtime.remoting>
</configuration> 

TIP

Configuration Files Configuration files are case-sensitive.

Server-Side Configuration

In Step-by-Step 3.7, I create a remoting server with Singleton activation mode, similar to one created in Step-by-Step 3.4. However, you'll note that the code itself is reduced, as most of the configuration-related code is now moved to a separate configuration file.

STEP BY STEP 3.7: Using Configuration Files to Register a Server-Activated Object in the Singleton Activation Mode

  1. Add a new Visual C# .NET console application named StepByStep3_7 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, right-click project StepByStep3_7 and select Add, Add New Item from the context menu. Add an item named StepByStep3_7.exe.config based on the XML file template.

  4. Open the StepByStep3_7.exe.config file and modify it to contain the following code:

    <configuration>
      <system.runtime.remoting>
        <application>
          <service>
            <!-- Set the activation mode,
            remotable object, and its URL -->
            <wellknown mode="Singleton"
              type=
           "StepByStep3_1.DbConnect, StepByStep3_1"
              objectUri="DbConnect" />
          </service>
          <channels>
            <!-- Set the channel and port -->
            <channel ref="tcp server"
              port="1234" />
          </channels>
        </application>
      </system.runtime.remoting>
    </configuration>
  5. In the Solution Explorer, select the project and click the Show All Files button in the toolbar. Move the StepByStep3_7.exe.config file from the project folder to the bin\Debug folder under the project where the StepByStep3_7.exe file will be created when the project is compiled.

  6. In the Solution Explorer, rename the default Class1.cs to DbConnectSingletonServer.cs. Open the file and change the name of the class to DbConnectSingletonServer in the class declaration.

  7. Add the following using directive:

    using System.Runtime.Remoting;
  8. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Load remoting configuration
      RemotingConfiguration.Configure
        ("StepByStep3_7.exe.config");
      Console.WriteLine("Started server in the " +
        "Singleton mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  9. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class for remote invocation by using the Singleton activation mode via its settings in the configuration file.

  10. Set StepByStep3_7, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode by retrieving the remoting configuration settings from the configuration file.

  11. Now set StepByStep3_3, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created when the form loads. The code binds the results from the remote method to the DataGrid control.

The most important thing to note in Step-by-Step 3.7 is the way I have written the <service> and <channels> elements in the configuration file.

The <service> element is written as follows:

<service>
  <!-- Set the activcation mode,
     remotable object, and its URL -->
  <wellknown mode="Singleton"
        type=
       "StepByStep3_1.DbConnect, StepByStep3_1"
       objectUri="DbConnect" />
</service>

For an SAO, you need to use the <wellknown> element, where you specify the activation mode, type, and the object URI. The type attribute is specified as a pair made up of the qualified classname (StepByStep3_1.DbConnect) and the name of the assembly (StepByStep3_1). The objectUri attribute specifies the endpoint of the URI where the client program will attempt to connect.

The <channels> element can be used to specify the channels the server uses to expose the remotable class. The ref attribute specifies the ID of the channel you want to use. The value ref="tcp server" specifies that the channel is a TCP server channel. If I instead write ref="tcp", the channel becomes a receiver-sender channel.

<channels>
  <!-- Set the channel and port -->
  <channel ref="tcp server" port="1234" />
</channels>

With the use of configuration files, the remoting code inside the Main() method of the server is now just one statement:

// Load remoting configuration
RemotingConfiguration.Configure(
  "StepByStep3_7.exe.config");

The Configure() method of the RemotingConfiguration class loads the configuration file into memory, parses its contents to locate the <system.runtime.remoting> section, and based on the settings, calls the relevant methods to register the channels and the remoting objects.

Client-Side Configuration

The configuration of the remoting client is quite similar to that of the remoting server. However, you configure the <client> element of the configuration file instead of the <service> element.

Step-by-Step 3.8 demonstrates how to use the client-side configuration files.

STEP BY STEP 3.8: Instantiating and Invoking a Server-Activated Object

  1. Add a new Visual C# .NET Windows application named StepByStep3_8 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, right-click project StepByStep3_8 and select Add, Add New Item from the context menu. Add an Item named StepByStep3_8.exe.config, based on the XML file template.

  4. Open the StepByStep3_8.exe.config file and modify it to contain the following code:

    <configuration>
      <system.runtime.remoting>
        <application>
          <client>
            <!-- Set the remotable object
            and its URL -->
            <wellknown
             type=
           "StepByStep3_1.DbConnect, StepByStep3_1"
             url="tcp://localhost:1234/DbConnect"
            />
          </client>
        </application>
      </system.runtime.remoting>
    </configuration>
  5. In the Solution Explorer, select the project and click the Show All Files button in the toolbar. Move the StepByStep3_8.exe.config file from the project folder to the bin\Debug folder under the project, where the StepByStep3_8.exe file will be created when the project is compiled.

  6. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  7. Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for this form's design.

  8. Add the following using directives:

    using System.Runtime.Remoting;
    using StepByStep3_1;
  9. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  10. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Load remoting configuration
      RemotingConfiguration.Configure
        ("StepByStep3_8.exe.config");
    
      // Instantiate the remote class
      dbc = new DbConnect();
    }
  11. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click(
      object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  12. Build the project. Set StepByStep3_7, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode by retrieving the remoting configuration settings from the configuration file.

  13. Set StepByStep3_8, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created from the settings stored in the configuration file. The code binds the results from the remote method to the DataGrid control.

The most important thing to note in Step-by-Step 3.8 is the use of the <client> element in the configuration file. The mode attribute is not part of the <wellknown> element of the client and the objectUri attribute is replaced with the url attribute that specifies the address to connect to the remote object.

<client>
  <!-- Set the remotable object
     and its URL -->
  <wellknown
      type=
       "StepByStep3_1.DbConnect, StepByStep3_1"
      url="tcp://localhost:1234/DbConnect"
  />
</client>

Unlike the server configuration file, the <channel> element is not required for the client because the client uses the URL to determine protocol and the port number.

Guided Practice Exercise 3.2

In this exercise, you are required to expose the DbConnect class from Step-by-Step 3.1 as a CAO through a remoting server through the HTTP channel. You also need to invoke methods on this client-activated object by creating a form similar to the one created in Step-by-Step 3.6.

However, you should be able to change various parameters such as the channel protocol, port number, URL, name, and type of the remote object without needing to recompile the server.

How would you design such a remoting client and server?

This exercise helps you practice creating configuration files for the remoting server and the client to remote a CAO. You should try working through this problem on your own first. If you get stuck, or if you'd like to see one possible solution, follow these steps:

  1. Add a new Visual C# .NET console application named GuidedPracticeExercise3_2_Server to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  3. In the Solution Explorer, right-click the project GuidedPracticeExercise3_2_Server and select Add, Add New Item from the context menu. Add an Item named GuidedPracticeExercise3_2_Server.exe.config based on the XML file template.

  4. Open the GuidedPracticeExercise3_2_Server.exe.config file and modify it to contain the following code:

    <configuration>
      <system.runtime.remoting>
        <application>
          <service>
            <!-- Set the remotable object -->
            <activated
              type=
           "StepByStep3_1.DbConnect, StepByStep3_1"
             />
          </service>
          <channels>
            <channel ref="http" port="1234" />
          </channels>
        </application>
      </system.runtime.remoting>
    </configuration>
  5. In the Solution Explorer, select the project and click the Show All Files button in the toolbar. Move the GuidedPracticeExercise3_2_Server.exe.config file from the project folder to the bin\Debug folder under the project, where the GuidedPracticeExercise3_2_Server.exe file will be created when the project is compiled.

  6. In the Solution Explorer, rename the default Class1.cs to DbConnectCAOServer.cs. Open the file and change the name of the class to DbConnectCAOServer in the class declaration.

  7. Add the following using directive:

    using System.Runtime.Remoting;
  8. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Load remoting configuration
      RemotingConfiguration.Configure
       ("GuidedPracticeExercise3_2_Server.exe.config");
    
      Console.WriteLine(
       "Started server in the Client Activation mode");
      Console.WriteLine(
       "Press <ENTER> to terminate server...");
      Console.ReadLine();
    } 
  9. Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class for remote invocation by using the client activation mode via its settings in the configuration file.

  10. Add a new Visual C# .NET Windows application named GuidedPracticeExercise3_2_Client to the solution.

  11. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).

  12. In the Solution Explorer, right-click the project GuidedPracticeExercise3_2_Client and select Add, Add New Item from the context menu. Add an Item named GuidedPracticeExercise3_2_Client.exe.config based on the XML file template.

  13. Open the GuidedPracticeExercise3_2_Client.exe.config file and modify it to contain the following code:

    <configuration>
      <system.runtime.remoting>
        <application>
          <!-- Set the url for the
           client activation -->
          <client url="http://localhost:1234">
            <!-- Set the remote object
            type and its assembly -->
            <activated
             type=
           "StepByStep3_1.DbConnect, StepByStep3_1"
            />
          </client>
        </application>
      </system.runtime.remoting>
    </configuration>
  14. In the Solution Explorer, select the project and click the Show All Files button in the tool bar. Move the GuidedPracticeExercise3_2_Client.exe.config file from the project folder to the bin\Debug folder under the project, where the GuidedPracticeExercise3_2_Client.exe file will be created when the project is compiled.

  15. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  16. Place three GroupBox controls (grpDatabases, grpQuery and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery, with its MultiLine property set to true), two Button controls (btnSelect and btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.10 for the design of this form.

  17. 17Select the Items property of the cboDatabases control in the Properties window and click on the (...) button. This opens the String Collection Editor dialog box. Enter the following database names in the editor:

    Northwind
    Pubs
    GrocerToGo 
  18. Click OK to add the databases to the Items collection of the cboDatabases control.

  19. 18Add the following using directives:

    using System.Runtime.Remoting;
    using StepByStep3_1;
  20. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  21. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      cboDatabases.SelectedIndex = 0;
      grpQuery.Enabled = false;
    }
  22. Double-click the btnSelect control and add the following code in the Click event handler:

    private void btnSelect_Click(
      object sender, System.EventArgs e)
    {
      grpDatabases.Enabled = false;
      grpQuery.Enabled = true;
    
      // Load remoting configuration
      RemotingConfiguration.Configure
       ("GuidedPracticeExercise3_2_Client.exe.config");
    
      // Instantiate the remote class
      dbc = new DbConnect(
        cboDatabases.SelectedItem.ToString());
    }
  23. Double-click the btnExecute control and add the following code in the Click event handler:

    private void btnExecute_Click(
      object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  24. Build the project. Set GuidedPracticeExercise3_2_Server, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the client activation mode by retrieving the remoting configuration settings from the configuration file.

  25. Set GuidedPracticeExercise3_2_Client, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created from the settings stored in the configuration file. The code binds the results from the remote method to the DataGrid control.

If you have difficulty following this exercise, review the sections "Creating a Client-Activated Object" and "Using Configuration Files to Configure the Remoting Framework" earlier in this chapter. After doing that review, try this exercise again.

Using Interface Assemblies to Compile Remoting Clients

So far, in all the examples, I had to copy the assembly containing the remotable class to the client project for the client to work. However, this is not a desirable solution in most cases because you may not want to share the implementation of the remotable object with your customers or business partners who are writing the client application.

An advisable solution in that case is to share only the interface of the remotable class with the client application. An interface does not contain any implementation; instead, it just contains the members that are supplied by the class.

In the following sections I demonstrate how you can create interfaces that will enable you to create remote SAOs and CAOs without sharing the implementation. I also introduce the Soapsuds tool, which can help you in automatically creating the interfaces for a remotable class.

Creating an Interface Assembly

In this section, I create an assembly that contains an interface named IDbConnect. The interface defines a contract (as shown in Step-by-Step 3.9). All the classes or structs that implement the interface must adhere to this contract.

STEP BY STEP 3.9: Creating an Interface Assembly

  1. Add a new Visual C# .NET class library named StepByStep3_9 to the solution.

  2. In the Solution Explorer, rename the default Class1.cs to IDbConnect.cs.

  3. Open the IDbConnect.cs and replace the code with the following code:

    using System;
    using System.Data;
    
    namespace StepByStep3_9
    {
      public interface IDbConnect
      {
        DataSet ExecuteQuery(string strQuery);
      }
    }
  4. Build the project. This step creates an assembly that contains the definition of the IDbConnect interface.

Creating a Remotable Object That Implements an Interface

Now that you have created the interface IDbConnect, you can implement this interface in a class named DbConnect. You must ensure that the class DbConnect adheres to the contract defined by the IDbConnect interface. This means you must implement the method ExecuteQuery() and the data types of parameters, and the return value must match with that defined in the interface.

Step-by-Step 3.10 creates the class DbConnect, which adheres to the IDbConnect interface.

STEP BY STEP 3.10: Creating a Remotable Object That Implements an Interface Assembly

  1. Add a new Visual C# .NET class library named StepByStep3_10 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_9 (the interface assembly).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnect.cs.

  4. Open the DbConnect.cs and replace the code with the following code:

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using StepByStep3_9;
    
    namespace StepByStep3_10
    {
      // Marshal-by-Reference Remotable Object
      // Inheriting from MarshalByRefObject and
      // Implementing IDbConnect interface
      public class DbConnect :
         MarshalByRefObject, IDbConnect
      {
        private SqlConnection sqlconn;
    
        // Default constructor connects to the
        // Northwind database
        public DbConnect() : this("Northwind")
        {
        }
    
        // Parameterized constructor connects to the
        // specified database
        public DbConnect(string DbName)
        {
          // Open a connection to the specified
          // sample SQL Server database
          sqlconn = new SqlConnection(
            "data source=(local);" +
            "initial catalog=" + DbName +
            ";integrated security=SSPI");
          Console.WriteLine(
            "Created a new connection to " +
            "the {0} database.", DbName);
        }
    
        public DataSet ExecuteQuery(string strQuery)
        {
          Console.Write("Starting to execute " +
            "the query...");
          // Create a SqlCommand object
          // to represent the query
          SqlCommand sqlcmd =
            sqlconn.CreateCommand();
          sqlcmd.CommandType = CommandType.Text;
          sqlcmd.CommandText = strQuery;
    
          // Create a SqlDataAdapter object
          // to talk to the database
          SqlDataAdapter sqlda =
            new SqlDataAdapter();
          sqlda.SelectCommand = sqlcmd;
          // Create a DataSet to hold the results
          DataSet ds = new DataSet();
          try
          {
            // Fill the DataSet
            sqlda.Fill(ds, "Results");
          }
          catch (Exception ex)
          {
            Console.WriteLine(ex.Message,
              "Error executing query");
          }
          Console.WriteLine("Done.");
          return ds;
        }
      }
    }
  5. Build the project. This step creates StepByStep3_10.DbConnect, a remotable class that implements the IDbConnect interface.

This program is similar to the one created in Step-by-Step 3.1, except that the DbConnect class is now implementing the IDbConnect interface in addition to deriving from the MarshalByRefObject class:

public class DbConnect :
  MarshalByRefObject, IDbConnect
{...}

You can expose this remotable object to the clients via the remoting framework by using the techniques that you already know. Step-by-Step 3.11 creates a remoting server that registers the DbConnect class created in Step-by-Step 3.10 as an SAO in Singleton mode.

STEP BY STEP 3.11: Creating a Remoting Server to Register the Remotable Object That Implements an Interface Assembly

  1. Add a new Visual C# .NET console application named StepByStep3_11 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, the project StepByStep3_9 (the interface assembly), and StepByStep3_10 (the remotable class assembly).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnectSingletonServer.cs. Open the file and change the name of the class to DbConnectSingletonServer in the class declaration.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
  5. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Register a TCP server channel that
      // listens on port 1234
      TcpServerChannel channel =
        new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      // Register the service that publishes
      // DbConnect for remote access in Singleton mode
      RemotingConfiguration.RegisterWellKnownServiceType
        (typeof(StepByStep3_10.DbConnect), "DbConnect",
        WellKnownObjectMode.Singleton);
      Console.WriteLine("Started server in the " +
        "Singleton mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  6. Build the project. This step creates a remoting server that is capable of registering StepByStep3_10.DbConnect, the remotable object that implements StepByStep3_9.IDbConnect interface, for remote invocation by using the Singleton activation mode.

Creating a Remoting Client That Uses an Interface Instead of the Implementation

When the remotable class is implementing an interface, you can include just the reference to the interface assembly rather than the implementation assembly itself at the client side. The client then extracts the necessary type information and metadata for compiling and running the program from the interface. Step-by-Step 3.12 shows you how to create such a client.

STEP BY STEP 3.12: Creating a Remoting Client to Invoke the Remotable Object That Implements an Interface Assembly

  1. Add a new Visual C# .NET Windows application named StepByStep3_12 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_9 (the interface assembly).

  3. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_9;
  5. Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of this form.

  6. Add the following code in the class definition:

    // Declare a Remote object
    IDbConnect dbc; 
  7. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Register a TCP client channel
      TcpClientChannel channel = new TcpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Instantiate the remote class
      dbc = (IDbConnect)
        Activator.GetObject(typeof(IDbConnect),
        @"tcp://localhost:1234/DbConnect");
    }
  8. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  9. Build the project. Set StepByStep3_11, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode.

  10. Now, set StepByStep3_12, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created when the form loads. The code binds the results from the remote method to the DataGrid control.

In the preceding program, you add the reference to StepByStep3_9.dll. This assembly contains the interface IDbConnect. However, an interface variable cannot be instantiated, and therefore you cannot create the instance of the remote object by using the new operator.

But there is an alternative way of creating instances of remote objects, which is especially helpful in the case of interfaces: You can use the methods of the Activator class of the System namespace as follows:

In Step-by-Step 3.12, I am using the Activator.GetObject() method to create a proxy for the server-activated object indicated by the type IDbConnect and the server URL.

Using the Soapsuds Tool to Automatically Generate an Interface Assembly

In the previous section, you learned how you can distribute the interface assemblies, rather than the implementation assembly, to the client, and still enable clients to instantiate the remote objects.

However, when you have large numbers of clients, distribution of interface files to each of them is another big issue. However, the .NET Framework SDK provides you the Soapsuds tool (soapsuds.exe) to overcome this issue.

Given the URL for a remotable object, the Soapsuds tool can automatically generate an interface assembly for the remote object. All the clients need to know is the URL of the remotable object. They can then generate interface assemblies on their own by using the Soapsuds tool. A typical usage of the soapsuds.exe is:

soapsuds -nowrappedproxy -urltoschema:http://MyServer.com/DbConnect?wsdl -
outputassemblyfile:DbConnectInterface.dll

NOTE

Wrapped Proxies Wrapped proxies are useful when you need to quickly test a Web service because with the use of wrapped proxies you don't have to write code for channel configuration and remote object registration. However, for better flexibility, you should use unwrapped proxies.

where the urltoschema (or url) switch specifies the remote object's URL, you normally have to append the URL with ?wsdl to enable the Soapsuds tool to generate the metadata from the URL. The outputassemblyfile (or oa) switch specifies the name of the file in which you want the output assembly to be created. The nowrappedproxy (or nwp) switch instructs the Soapsuds tool to generate an unwrapped proxy.

TIP

Soapsuds Tool The Soapsuds tool can be used only with the HTTP channel. If you are using the TCP channel, you still need to depend on manually creating and distributing the interfaces for remotable classes.

By default the Soapsuds tool generates wrapped proxies. Wrapped proxies are the proxies that store various connection details such as the channel formatting and the URL within the proxy itself. Although this means that you need not write the code for these things, this is not recommended because it reduces the flexibility to change the configuration. If you want to avoid specifying these details in the code, a better way is to use the configuration files.

In Step-by-Step 3.13 you create a client that uses the soapsuds-generated unwrapped proxy to connect with the HTTP server created in Guided Practice Exercise 3.1.

STEP BY STEP 3.13: Using the Soapsuds Tool (soapsuds.exe) to Automatically Generate an Interface Assembly

  1. Set the GuidedPracticeExercise3_1_Server as the startup project. Select Debug, Start Without Debugging to run the project.

  2. Select Start, Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, Visual Studio .NET Command Prompt to launch a .NET command prompt.

  3. Make sure that GuidedPracticeExercise3_1_Server is running. Navigate to the StepByStep3_13 project and run the following command to automatically generate an interface assembly for the remotable object DbConnect, registered by the remoting server, GuidedPracticeExercise3_1_Server:

    soapsuds -url:http://localhost:1234/DbConnect?wsdl 
    -oa:DbConnectInterface.dll -nowp
  4. Add references to the .NET assembly System.Runtime.Remoting and DbConnectInterface.dll (the interface assembly auto-generated by soapsuds.exe in step 3).

  5. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  6. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Http;
    using StepByStep3_1;
  7. Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of this form.

  8. Add the following code in the class definition:

    // Declare a Remote object
    DbConnect dbc; 
  9. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // This step is not required if you use
      // a wrapped proxy
      // Register an HTTP client channel
      HttpClientChannel channel =
        new HttpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // This step is not required if you use
      // a wrapped proxy.
      // Register the remote class as a valid
      // type in the client's application domain
      RemotingConfiguration.RegisterWellKnownClientType(
        // Remote class
        typeof(DbConnect),
        // URL of the remote class
        "http://localhost:1234/DbConnect");
    
      // Instantiate the remote class
      dbc = new DbConnect();
    }
  10. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  11. Build the project. If the GuidedPracticeExercise3_1_Server project is not running, set the GuidedPracticeExercise3_1_Server as the startup project and select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the Singleton activation mode.

  12. Set StepByStep3_13, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created when the form loads. The code binds the results from the remote method to the DataGrid control.

In Step-by-Step 3.13, you learned how to use the Soapsuds tool to automatically generate the interface for the remotable object.

TIP

Soapsuds Tool and Client-Activated Objects You cannot use the Soapsuds tool with the client-activated object because the metadata generated by the Soapsuds tool can use only the default constructor to create the remote objects.

Also, the interface generated by the Soapsuds tool enables you to directly use the name of the remotable class, which means you can access the remotable class directly in the client as if you had a direct reference to the remotable class at the client side. Therefore you can use the RegisterWellKnownClientType() method of the RemotingConfiguration class, rather than the Activator.GetObject() method, to register the remote object on the client side.

Creating an Interface Assembly That Works with the Client-Activated Objects

Both the techniques for generating interface assemblies discussed so far, in the sections, "Creating an Interface Assembly" and "Using the Soapsuds Tool to Automatically Generate an Interface Assembly" are not useful for CAO.

The problem lies with the constructors. The CAOs have capabilities of invoking even the non-default constructors of the remotable class. However, interfaces cannot contain the declaration for a constructor because they contain only method and property declarations.

This common problem is generally solved by following these steps:

  1. Create an interface and the remotable class as shown in the previous examples. For easy reference, I'll call them IDbConnect and DbConnect, respectively. IDbConnect contains definitions of all the methods that the DbConnect class wants exposed to the clients.

  2. Create an interface that declares as many different methods as there are constructors in the remotable class. Each method is responsible for creating an object in a way defined by its corresponding constructor. This technique is also called as an abstract factory pattern. The name contains the word "factory" because it enables you to create objects in different ways. Let's call this interface IDbConnectFactory.

  3. Create a class that derives from the MarshalByRefObject class and implements the interface created in step 2 (IDbConnectFactory). Implement all methods to actually create the remotable object (DbConnect) based on the given parameters and return the created instance (DbConnect). Let's call this class DbConnectFactory.

  4. Create a remoting server that registers the class created in step 3 (DbConnectFactory) as the remotable class.

  5. Create a client that connects to the server and create an instance of the remotable class created in step 3 (DbConnectFactory).

  6. Invoke the methods corresponding to a constructor on the remotable object created in step 5. The return value of the constructor is a remotable object of the type defined in step 1 (DbConnect).

  7. You now have an object of a type that you originally wanted to remote (DbConnect). You can use this object to invoke methods.

In this section I show you how to implement the preceding technique to create a client-activated object that allows you to use any of its available constructors to create objects.

I use the same remotable class that I defined in Step-by-Step 3.10 (DbConnect), and its interface (IDbConnect), which I defined in Step-by-Step 3.9. I'll start directly with Step 2 of preceding list by creating an IDbConnectFactory interface in Step-by-Step 3.14.

STEP BY STEP 3.14: Creating an Assembly That Works As an Abstract Factory for the IDbConnect Objects.

  1. Add a new Visual C# .NET class library named StepByStep3_14 to the solution.

  2. Add references to the project StepByStep3_9 (the interface assembly).

  3. In the Solution Explorer, rename the default Class1.cs to IDbConnectFactory.cs.

  4. Open the IDbConnectFactory.cs and replace the code with the following code:

    using System;
    using System.Data;
    using StepByStep3_9;
    
    namespace StepByStep3_14
    {
      public interface IDbConnectFactory
      {
        IDbConnect CreateDbConnectInstance();
        IDbConnect CreateDbConnectInstance(
          string dbName);
      }
    }
  5. Build the project. The class library now contains an interface to a class that can act as a factory of objects implementing the IDbConnect interface.

The next step is to create a class that implements the IDbConnectFactory interface and then expose that class as a remotable class through the remoting framework. Step-by-Step 3.15 shows the steps for doing so.

STEP BY STEP 3.15: Creating a Remoting Server That Exposes the DbConnectFactory As a Remotable Class

  1. Add a new Visual C# .NET console application named StepByStep3_15 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, the project StepByStep3_9 (the interface assembly containing IDbConnect) StepByStep3_14 (the new interface assembly containing the IDbConnectFactory) and StepByStep3_10 (the remotable class assembly).

  3. Add a new class to the project that will create another remotable object that inherits from MarshalByRefObject class and implements the IDbConnectFactory interface, created in Step-by-Step 3.14. Name it DbConnectFactory.cs. Open the DbConnectFactory.cs and replace the code with the following code:

    using System;
    using StepByStep3_9;
    using StepByStep3_10;
    using StepByStep3_14;
    
    namespace StepByStep3_15
    {
      class DbConnectFactory :
        MarshalByRefObject, IDbConnectFactory
      {
        public IDbConnect CreateDbConnectInstance()
        {
          return new DbConnect();
        }
        public IDbConnect CreateDbConnectInstance(
          string dbName)
        {
          return new DbConnect(dbName);
        }
      }
    }
  4. In the Solution Explorer, rename the default Class1.cs to DbConnectFactoryServer.cs. Open the file and change the name of the class to DbConnectFactoryServer in the class declaration.

  5. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
  6. Add the following code in the Main() method:

    [STAThread]
    static void Main(string[] args)
    {
      // Register a TCP server channel that
      // listens on port 1234
      TcpServerChannel channel =
        new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(channel);
    
      RemotingConfiguration.RegisterWellKnownServiceType
        (typeof(DbConnectFactory),
        "DbConnectFactory",
        WellKnownObjectMode.Singleton);
      Console.WriteLine("Started server in " +
        "client-activated mode");
      Console.WriteLine("Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  7. Build the project. This step uses the Singleton activation mode to create a remoting server that is capable of registering StepByStep3_15.DbConnectFactory, the remotable object that implements the StepByStep3_14. IDbConnectFactory interface, for remote invocation.

Step-by-Step 3.15 exposes the DbConnectFactory as an SAO in Singleton mode. Although the DbConnectFactory object is registered as an SAO, the objects returned by various methods of DbConnectFactory (DbConnect) are CAOs because they are created only on an explicit request from the client.

Step-by-Step 3.16 is the final step for creating a CAO using the abstract factory pattern.

STEP BY STEP 3.16: Using the Abstract Factory Pattern to Instantiate and Invoke a Client-Activated Object

  1. Add a new Visual C# .NET Windows application named StepByStep3_16 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the projects StepByStep3_9 (the IDbConnect interface assembly) and StepByStep3_14 (the IDbConnectFactory interface assembly).

  3. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_9; // contains IDbConnect
    using StepByStep3_14; // contains IDbConnectFactory
  5. Place three GroupBox controls (grpDatabases, grpQuery, and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery, with its MultiLine property set to true), two Button controls (btnSelect and btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.10 for this form's design.

  6. Select the Items property of the cboDatabases control in the Properties window and click on the (...) button. This opens the String Collection Editor dialog box. Enter the following database names in the editor:

    Northwind
    Pubs
    GrocerToGo 

    Click OK to add the databases to the Items collection of the cboDatabases control.

  7. Add the following code in the class definition:

    // Declare a Remote object
    IDbConnect dbc; 
  8. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      cboDatabases.SelectedIndex = 0;
      grpQuery.Enabled = false;
    }
  9. Double-click the btnSelect control and add the following code in the Click event handler:

    private void btnSelect_Click(
      object sender, System.EventArgs e)
    {
      // Disable the Databases group box and
      // enable the Query group box
      grpDatabases.Enabled = false;
      grpQuery.Enabled = true;
    
      // Register a TCP client channel
      TcpClientChannel channel = new TcpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Register the remote class as a valid
      // type in the client's application domain
      // by passing the Remote class and its URL
      IDbConnectFactory dbcf = (IDbConnectFactory)
         Activator.GetObject(typeof(IDbConnectFactory),
          "tcp://localhost:1234/DbConnectFactory");
    
      dbc = dbcf.CreateDbConnectInstance
         (cboDatabases.SelectedItem.ToString());
    }
  10. Double-click the btnExecute control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  11. Build the project. This step creates a remoting client that is capable of activating DbConnect as a CAO.

  12. Set StepByStep3_15, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in client-activation mode.

  13. Set StepByStep3_16, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Select a database from the combo box and click the Select button. An instance of the remotable object, DbConnect is created with the selected database. Now enter a query in the text box and click the button. The code invokes a method on the remote object. The code binds the results from the remote method to the DataGrid control.

  14. Again select Debug, Start Without Debugging to run the remoting client. Select a different database from the combo box and click the Select button. An instance of the remotable object, DbConnect, is created with the selected database. Now enter a query in the text box and click the button. You should see that the second instance of the client fetches the data from the selected database. Now switch to the Server command window. You see that two times the remote object is instantiated with different databases, as shown earlier in Figure 3.10. This shows that client activation creates an instance of the remotable object per client.

In Step-by-Step 3.16, it is important to note that although the object for DbConnectFactory is created as a server-activated object, the DbConnect object is always created as the client-activated object. For the DbConnectFactory object, there is only one instance for all the clients; however, there is one DbConnect object for each client.

REVIEW BREAK

Using IIS As an Activation Agent

Create and consume a .NET Remoting object: Create the listener service.

So far in this chapter, you are hosting the remotable class by creating your own server that is a console application. The major disadvantage with this approach is that you have to manually start the server if it is not already running.

As discussed before, remoting provides two alternatives to overcome this disadvantage: You can either run the server process as a Windows service instead of a console application, or use IIS as an activation agent (which is a built-in Windows service) for the server process. I'll talk about the former in Chapter 6, and the latter in this section.

Using IIS as an activation agent offers the following advantages:

The following list specifies what you need to do to host a remotable class in IIS:

TIP

IIS Does Not Support CAO When creating IIS-hosted remote objects, you cannot specify constructor parameters; therefore it is not possible to use IIS to activate CAO.

NOTE

Channels and IIS Activation IIS Activation supports only HTTP channels. The default formatting is SOAP, but IIS also supports binary and other custom formatting.

Step-by-Step 3.17 demonstrates how to use IIS for activating the DbConnect remotable class from Step-by-Step 3.10.

STEP BY STEP 3.17: Using IIS As an Activation Agent

  1. Add a new empty Web project named StepByStep3_17 to the solution.

  2. Add references to the project StepByStep3_9 (the interface assembly) and StepByStep3_10 (the remotable object) by selecting Add Reference from the context menu.

  3. Add a Web configuration file to the project. Open the web.config file and add the following <system.runtime.remoting> element inside the <configuration> element:

    <configuration>
      <system.runtime.remoting>
        <application>
          <service>
          <!-- Set the activation mode,
            remotable object, and its URL -->
            <wellknown mode="Singleton"
              type="StepByStep3_10.DbConnect, StepByStep3_10"
              objectUri="DbConnect.rem"
            />
          </service>
        </application>
      </system.runtime.remoting>
    ...
    </configuration>
  4. IIS is now hosting the StepByStep3_10.DbConnect remotable class as a server-activated object by using the Singleton activation mode.

Note that the objectUri of the SAO in the preceding example ends with the extension .rem.

Step-by-Step 3.18 demonstrates how to invoke a remote object hosted in the IIS.

STEP BY STEP 3.18: Instantiating and Invoking an IIS-Hosted Remote Object

  1. Add a new Visual C# .NET Windows application named StepByStep3_18 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_9 (the interface assembly).

  3. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  4. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Http;
    using StepByStep3_9; // contains IDbConnect
  5. Place two GroupBox controls, a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of this form.

  6. Add the following code in the class definition:

    // Declare a Remote object
    IDbConnect dbc; 
  7. Double-click the form and add the following code in the Load event handler:

    private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Register an HTTP client channel
      HttpClientChannel channel =
        new HttpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Instantiate the remote class
      dbc = (IDbConnect)
        Activator.GetObject(typeof(IDbConnect),
      @"http://localhost/StepByStep3_17/DbConnect.rem");
    }
  8. Double-click the Button control and add the following code in the Click event handler:

    private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  // Invoke a method on the remote object
        this.dgResults.DataSource =
          dbc.ExecuteQuery(this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message,
          "Query Execution Error");
      }
    }
  9. Build the project. This step creates the remoting client that can invoke an IIS-hosted remote object.

  10. Set StepByStep3_18, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object, which is created when the form loads. The code binds the results from the remote method to the DataGrid control.

This client was similar to the client created in Step-by-Step 3.12; however, there are a few things to note. The client is using the HTTP channel to communicate with the server, there is no port number specified in the URL, and the URL ends with .rem.

Asynchronous Remoting

Create and consume a .NET Remoting object: Implement an asynchronous method.

So far, all the method invocations that you've studied in this chapter have been synchronous. In a synchronous method call, the thread that executes the method call waits until the called method finishes execution.

NOTE

Asynchronous Method Calls For asynchronous method calls, the remote objects need not explicitly support the asynchronous behavior. It is up to the caller to decide whether a particular remote call is asynchronous or not.

Such types of synchronous calls can make the user interface very non-responsive if the client program is waiting for a long process to finish executing on the server. This is especially true for remote method calls where additional time is involved because the calls are made across the network.

The .NET framework has a solution for this in the form of asynchronous methods. An asynchronous method calls the method and returns immediately, leaving the invoked method to complete its execution.

Understanding the Model of Asynchronous Programming in the .NET Framework

In the .NET Framework, asynchronous programming is implemented with the help of delegate types. Delegates are types that are capable of storing references to methods of a specific signature. A delegate declared as follows is capable of invoking methods that take a string parameter and return a string type:

delegate string LongProcessDelegate(string param);

So if there is a method definition, such as the following:

string LongProcess(string param)
{
...
}

then the LongProcessDelegate can hold references to this method like this:

LongProcessDelegate delLongProcess;
delLongProcess = new LongProcessDelegate(LongProcess);

After you have the delegate object available, you can call its BeginInvoke() method to call the LongProcess() method asynchronously, such as in the following:

IAsyncResult ar =
 delLongProcess.BeginInvoke("Test", null, null);

Here the IAsyncResult interface is used to monitor an asynchronous call and relate the beginning and the end of an asynchronous method call. When you use the BeginInvoke() method, the control immediately comes back to the next statement while the LongProcess() method may still be executing.

To return the value of an asynchronous method call, you can call the EndInvoke() method on the same delegate, such as in the following:

String result = delLongProcess.EndInvoke(ar);

However, it is important to know where to place this method call, because when you call the EndInvoke() method, if the LongProcess() method has not yet completed execution, the EndInvoke() method causes the current thread to wait for the completion of the LongProcess() method. A poor use of the EndInvoke() method, such as placing it in just the next statement after the BeginInvoke() method, can cause an asynchronous method call to result in a synchronous method call.

One of the alternatives to this problem is to use the IsCompleted property of the IAsyncResult object to check whether the method has completed the execution, and then call the EndInvoke() method only in such a case.

string result;
if(ar.IsCompleted)
{
  result = delLongProcess.EndInvoke(ar);
}

But regular polling of the IAsyncResult.IsCompleted property requires additional work at the client side.

You can also use a WaitHandle object to manage asynchronous remote method calls. When you are ready to wait for the results of the remote method call, you can retrieve a WaitHandle object from the IAsyncResult object's AsyncWaitHandle property:

ar.AsyncWaitHandle.WaitOne();
String result = delLongProcess.EndInvoke(ar);

The WaitOne() method of the WaitHandle object causes the thread to pause until the results are ready. The WaitHandle object has other methods that are useful if you have multiple outstanding asynchronous remote method calls. You can wait for all the method calls to come back by using the static WaitHandle.WaitAll() method, or for the first one to return by using the static WaitHandle.WaitAny() method. Therefore, the WaitHandle object essentially lets you turn the asynchronous process back into a synchronous process.

There is, in fact, a better technique for managing asynchronous method invocation—callback methods. In this technique, you can register a method that is automatically invoked as soon as the remote method finishes execution. You can then place a call to the EndInvoke() method inside the callback method to collect the result of remote method execution.

To implement the callback technique with an asynchronous method invocation, you need to take the following steps in the client program:

  1. Define a callback method that you want to execute when the remote method has finished execution.

  2. Create an object of delegate type AsyncCallback to store the reference to the method created in step 1.

  3. Create an instance of the remote object on which you wish to invoke remote method calls.

  4. Declare a delegate type capable of storing references to the remote method.

  5. Using the object in step 3, create a new instance of delegate declared in step 4 to refer to the remote method.

  6. Call the BeginInvoke() method on the delegate created in step 5, passing any arguments and the AsyncCallback object.

  7. Wait for the server object to call your callback method when the method has completed.

Applying Asynchronous Programming

In the following sections I create a set of three projects to demonstrate the use of the callback method for an asynchronous method call:

I create a remotable class that is different from other remotable classes used in this chapter to help you properly understand synchronous and asynchronous method calls.

The remotable class used in Step-by-Step 3.19 is named RemotableClass and it has a single method named LongProcess(). The LongProcess() method waits for about 5 seconds to simulate a long process call.

STEP BY STEP 3.19: Creating a Remotable Class

  1. Add a new Visual C# .NET class library named StepByStep3_19 to the solution.

  2. In the Solution Explorer, rename the default Class1.cs to RemotableClass.cs.

  3. Open the RemotableClass.cs and replace the code with the following code:

    using System;
    using System.Threading;
    
    namespace StepByStep3_19
    {
      public class RemotableClass : MarshalByRefObject
      {
        public string LongProcess(string param)
        {
          Thread.Sleep(5000);
          return param;
        }
      }
    }
  4. Select Build, Build StepByStep3_19. This step generates the code for your class library and packages it into the file StepByStep3_19.dll, which is located in the bin\Debug or bin\Release directory of your project.

RemotableClass is now ready to be hosted in a remoting host. In Step-by-Step 3.20, I decided to use IIS to host the remotable class because it requires only a minimum amount of code. I control the remoting configuration by modifying the web.config file.

STEP BY STEP 3.20: Using IIS to Host a Remotable Class

  1. Add a new Visual C# ASP.NET application named StepByStep3_20 to the solution.

  2. Add references the project StepByStep3_19 (the remotable class assembly).

  3. Open the web.config file and add the following <system.runtime.remoting> element inside the <configuration> element:

    <configuration>
     <system.runtime.remoting>
       <application>
        <service>
          <!-- Set the activation mode,
            remotable object, and its URL -->
          <wellknown mode="Singleton"
            type="StepByStep3_19.RemotableClass, StepByStep3_19"
            objectUri="RemotableClass.rem" />
        </service>
       </application>
     </system.runtime.remoting>
    ...
    </configuration>
  4. The remoting server is now hosting the RemotableClass contained in the assembly StepByStep3_19 for remote invocation by using the Singleton activation mode.

Both RemotableClass, the remote object and the remoting host contain no additional information that supports an asynchronous method call. The asynchronous call is completely dependent on the client code. So in the final step, I create a client application that calls a remote method synchronously as well as asynchronously. Seeing both ways of calling a method will help you note the difference.

STEP BY STEP 3.21: Instantiating and Invoking a Client That Calls Remote Object Methods Synchronously and Asynchronously

  1. Add a new Visual C# .NET Windows application named StepByStep3_21 to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting.

  3. Select Start, Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, Visual Studio .NET Command Prompt to launch a .NET command prompt.

  4. Navigate to the StepByStep3_13 project and run the following command to automatically generate the source code for the interface of the remotable class RemotableClass, registered by the IIS Web server:

    soapsuds -nowp –gc 
    -url:http://localhost/StepByStep3_20/RemotableClass. rem?wsdl
    

    This step generates a file named StepByStep3_19.cs, which contains the interface code for the RemotableClass class.

  5. 5In the Solution Explorer, right-click project StepByStep3_21 and select Add, Add Existing Item to add the StepByStep3_19.cs file (generated by the soapsuds.exe in step 4).

  6. Open the file StepByStep3_19.cs. You will notice that the RemotableClass class definition is applied with two attributes—Serializable and SoapType. Remove the SoapType attribute definition so that the class definition looks like this:

    [Serializable]
    public class RemotableClass :
      System.MarshalByRefObject
    {
      ...
    }
  7. In the Solution Explorer, rename the default Form1.cs to SyncAsync.cs. Open the form in code view and change all occurrences of Form1 to refer to SyncAsync instead.

  8. Add the following using directives:

    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Http;
    // Contains RemotableClass interface
    using StepByStep3_19; 
  9. Place a Label control, a TextBox control (txtResults), and two Button controls (btnSync and btnAsync) on the form. Refer to Figure 3.12 for the design of this form.

  10. Add the following code to the class definition:

    // Remotable object
    RemotableClass remObject;
    
    // Create a delegate for the LongProcess method
    // of the Remotable object
    delegate string LongProcessDelegate(string param);
    
    // Declare a LongProcessDelegate object,
    // an AsyncCallback delegate object,
    // and an IAsyncResult object
    LongProcessDelegate delLongProcess;
    AsyncCallback ab;
    IAsyncResult ar;
    
    // Declare a static integer variable to hold
    // the number of times the method is called
    static int counter; 
  11. Double-click the form and add the following code in the Load event handler:

    private void SyncAsync_Load(
      object sender, System.EventArgs e)
    {
      // Register an HTTP client channel
      HttpClientChannel channel =
        new HttpClientChannel();
      ChannelServices.RegisterChannel(channel);
    
      // Instantiate the remote class
      remObject = (RemotableClass) Activator.GetObject(
        typeof(RemotableClass),
        @"http://localhost/StepByStep3_20/RemotableClass.rem");
    
      // Create an AsyncCallback delegate object to hold
      // the reference of the LongProcessCompleted
      // callback method, which is called when
      // the asynchronous call is completed
      ab = new AsyncCallback(LongProcessCompleted);
    
      // Create a LongProcessDelegate delegate object to
      // hold the reference of the LongProcess method
      delLongProcess = new LongProcessDelegate(
        remObject.LongProcess);
    }
  12. Double-click the btnSync control and add the following code in the Click event handler:

    private void btnSync_Click(
      object sender, System.EventArgs e)
    {
      // Increment the method call counter
      counter++;
      string param = String.Format(
        "Call: {0}, Type=Synchronous", counter);
    
      // Append the start message to the text box
      txtResults.AppendText(String.Format(
        "{0}, Started at: {1}\n",
        param, DateTime.Now.ToLongTimeString()));
    
      // Call the LongProcess method of the
      // remotable object
      remObject.LongProcess(param);
    
      // Append the completed message to the text box
      txtResults.AppendText(
        String.Format("{0}, Completed at: {1}\n",
        param, DateTime.Now.ToLongTimeString()));
    }
  13. Double-click the btnAsync control and add the following code in the Click event handler:

    private void btnAsync_Click(
      object sender, System.EventArgs e)
    {
      // Increment the method call counter
      counter++;
      string param = String.Format(
        "Call: {0}, Type=Asynchronous",
        counter);
      // Append the start message to the text box
      txtResults.AppendText(
        String.Format("{0}, Started at: {1}\n",
        param, DateTime.Now.ToLongTimeString()));
    
      // Call the BeginInvoke method to start
      // the asynchronous method call
      ar = delLongProcess.BeginInvoke(param, ab, null);
    }
  14. Add the following callback method definition for LongProcessCompleted() to the class:

    void LongProcessCompleted(IAsyncResult ar)
    {
      // Call the EndInvoke method to retrieve the
      // return value of the asynchronous method call
      string result = delLongProcess.EndInvoke(ar);
    
      // Append the completed message to the text box
      txtResults.AppendText(
        String.Format("{0}, Completed at: {1}\n",
        result, DateTime.Now.ToLongTimeString()));
    }
  15. Build the project. This step creates a remoting client for the RemotableClass remotable object.

  16. Set StepByStep3_21, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Click the Call Long Process Synchronously button to call the LongProcess() method synchronously. You should see the start message appended to the text box, and the application freezes for the next five seconds. After the method call is completed you notice a completed message and you are now able to work with the application. Now click the Call Long Process Asynchronously button to call the LongProcess() method asynchronously. As soon as the method starts, the start message is appended to the text box. While the method is executing, you can still work with the application (such as by moving the form, clicking buttons, and so on, although clicking the synchronous call button freezes the application). When the method is completed, you notice a completed message in the text box, as shown in Figure 3.12.

Figure 3.12 The asynchronous method call invokes the method and transfers the control back to the form. The asynchronous call can use a callback method to get a notification when the remote method finishes execution.

In this program I am following the same steps for an asynchronous method call that were discussed earlier in the previous section, "Understanding the Model of Asynchronous Programming in the .NET Framework."

The only surprise in Step-by-Step 3.21 is the special use of the Soapsuds tool. Step 4 uses the Soapsuds tool to generate the source code of the interface assembly instead of the assembly itself. The soapsuds-generated assembly uses a SoapType attribute that doesn't work with delegates. The advantage of soapsuds-generated source code is that I can manually remove the problematic SoapType attribute and compile the program to generate the interface assembly.

REVIEW BREAK

Chapter Summary

The .NET Framework is a modern platform for building distributed applications. In this chapter you learned how to create distributed applications by using the remoting library of the .NET Framework.

You learned that the remoting framework is made up of several elements, such as remotable classes, remoting hosts, remoting clients, channels, formatters, and so on. Each of these elements is configurable and extensible. You practiced creating a remotable class and hosting that class in several modes such as server-activated SingleCall, server-activated Singleton, and client-activated. You also used different channels, such as HTTP and TCP, and different formatters, such as SOAP and binary.

I also demonstrated the use of a configuration file to configure the remoting configuration for a remoting host and the remoting client. You noted that depending on your requirements, you can write remoting configurations at two level: one at the application level (.exe.config, or web.config for IIS) and the other at the machine level (machine.config).

Finally, I discussed the technique of invoking remote methods asynchronously. An asynchronous method call makes a user interface–based application, such as a Windows forms application, quite responsive to user input despite the delays caused in a remote method call.

Key Terms

Apply Your Knowledge

Exercises

3.1 - Using HTTP Channels with Binary Formatters

As I discussed in the chapter, the default formatter for the HTTP channel is SOAP, and for the TCP channel is Binary. The formatters are used for serializing and deserializing messages in the specified encoding.

The chapter has made use of these default formatters in the examples. In this exercise, you'll learn how to configure a channel to use a formatter different from the default one.

In particular I'll demonstrate how to use the binary formatter with the HTTP channel. This combination is especially useful when you want to optimize the performance of a remote object that is hosted on IIS. However, you should note that binary format is proprietary to the .NET Framework.

Estimated Time: 30 minutes.

  1. Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 320C03Exercises. Click OK.

  2. Add a new empty Web project named Exercise3_1_Server to the solution.

  3. Add references to StepByStep3_9.dll (the interface assembly containing IDbConnect) and StepByStep3_10.dll (the remotable object, DbConnect).

  4. Add a new Web configuration file to the project. Open the web.config file and add the following <system.runtime.remoting> element inside the <configuration> element:

    <configuration>
      <system.runtime.remoting>
        <application>
          <service>
            <!--Set the activation mode,
             remotable object, 
             and its URL -->
            <wellknown mode="Singleton"
              type=
             "StepByStep3_10.DbConnect, StepByStep3_10"
              objectUri=
                "DbConnect.rem" />
          </service>
        </application>
      </system.runtime.remoting> 
    ...
    </configuration>

    IIS is now hosting the StepByStep3_10.DbConnect remotable class as a server-activated object by using the Singleton activation mode.

  5. Add a new Visual C# .NET Windows application named Exercise3_1_Client to the solution.

  6. Add references to StepByStep3_9.dll (the interface assembly containing IDbConnect).

  7. In the Solution Explorer, right-click project Exercise3_1_Client and select Add, Add New Item from the context menu. Add an item named Exercise3_1_Client.exe.config, based on the XML file template.

  8. Open the Exercise3_1_Client.exe.config file and modify it to contain the following code:

  9. <configuration>
      <system.runtime.remoting>
        <application>
          <channels>
            <channel ref="http">
              <serverProviders>
                <formatter ref = 
                   "binary" />
              </serverProviders>
            </channel>
          </channels>
        </application>
      </system.runtime.remoting>
    </configuration>
  10. In the Solution Explorer, select the project and click the Show All Files button in the tool bar. Move the Exercise3_1_Client.exe.config file from the project folder to the bin\Debug folder under the project, where the Exercise3_1_Client.exe file will be created when the project is compiled.

  11. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  12. Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery, with its MultiLine property set to true), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of this form.

  13. Add the following using directives:

  14. using System.Runtime.Remoting;
    using StepByStep3_9;
  15. Add the following code in the class definition:

  16. // Declare a Remote object
    IDbConnect dbc; 
  17. Double-click the form and add the following code in the Load event handler:

  18. private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Load remoting configuration 
      RemotingConfiguration.Configure
        ("Exercise3_1_Client.exe.config");
    
      // Instantiate the remote class
      dbc = (IDbConnect)
      Activator.GetObject(typeof(IDbConnect),
      @"http://localhost/Exercise3_1_Server/DbConnect.rem");
    }
  19. Double-click the Button control and add the following code in the Click event handler:

  20. private void btnExecute_Click(
      object sender, System.EventArgs e)
    {
      try
      {  
        // Invoke a method on 
        // the remote object
        this.dgResults.DataSource = 
          dbc.ExecuteQuery(
            this.txtQuery.Text);
        dgResults.DataMember = "Results";
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message, 
          "Query Execution Error");
      }
    }
  21. Build the project. Set Exercise3_1_Client, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Enter a query in the text box and click the button. The code invokes a method on the remote object. The remote object is serialized and deserialized in binary format and is transported over the HTTP. The code binds the results from the remote method to the DataGrid control.

Note that you can specify the desired formatter for the client and need not specify the formatter for the server. The formatter requested by the client will be used to format data by the server for that client.

3.2 - Dynamically Publishing a Well-Known Object

Well-known objects cannot be invoked from a client with a non-default constructor. However, you can create an object using any constructor you wish, initialize it any way you wish, and then make it available to clients.

You should use the RemotingServices.Marshal() method to publish an existing object instance.

Estimated Time: 30 minutes.

  1. Add a new Visual C# console application named Exercise3_2_Server to the solution.

  2. Add references to the .NET assembly System.Runtime.Remoting, the StepByStep3_9.dll (the interface assembly containing IDbConnect), and StepByStep3_10.dll (the remotable object, DbConnect).

  3. In the Solution Explorer, rename the default Class1.cs to DbConnectServer.cs. Open the file and change the name of the class to DbConnectServer in the class declaration.

  4. Add the following using directives:

  5. using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_10;
  6. Add the following code in the Main() method:

  7. [STAThread]
    static void Main(string[] args)
    {
      // Create and register a TCP server 
      // channel that listens on port 1234
      TcpServerChannel channel = 
        new TcpServerChannel(1234);
      ChannelServices.RegisterChannel(
        channel);
    
      // Create the remotable object here 
      // itself. Call the 
      // RemotingServices.Marshal() method
      // to marshal (serialize) the created 
      // remotable object to transfer the 
      // object beyond application boundaries
      // with the specified uri
      DbConnect dbcPubs = 
        new DbConnect("Pubs");
      RemotingServices.Marshal(
        dbcPubs, "Pubs.uri");
    
      DbConnect dbcNorthwind = 
        new DbConnect("Northwind");
      RemotingServices.Marshal(
        dbcNorthwind, "Northwind.uri");
    
      Console.WriteLine(
        "Started server in the " +
        "Singleton mode");
      Console.WriteLine(
        "Press <ENTER> to terminate " +
        "server...");
      Console.ReadLine();
    } 
  8. Build the project. This step creates a remoting server that creates the remotable object StepByStep3_1.DbConnect and is capable of marshaling the remote object across application boundaries.

  9. Add a new Visual C# .NET Windows application named Exercise3_2_Client to the solution.

  10. Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_9 (the interface assembly).

  11. In the Solution Explorer, rename the default Form1.cs to DbConnectClient.cs. Open the form in code view and change all occurrences of Form1 to refer to DbConnectClient instead.

  12. Add the following using directives:

  13. using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using StepByStep3_9;
  14. Place three GroupBox controls (grpDatabases, grpQuery, and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Set the DropDownStyle to DropDownList.

  15. Select the Items property of the cboDatabases control in the Properties window and click on the (...) button. This opens the String Collection Editor dialog box. Enter the following database names in the editor:

  16. Northwind
    Pubs
  17. Click OK to add the databases to the Items collection of the cboDatabases control.

  18. Add the following code in the class definition:

  19. // Declare a remote object
    IDbConnect dbc; 
  20. Double-click the form and add the following code in the Load event handler:

  21. private void DbConnectClient_Load(
      object sender, System.EventArgs e)
    {
      // Register a TCP client channel 
      TcpClientChannel channel = 
        new TcpClientChannel();
      ChannelServices.RegisterChannel(
        channel);
    
      cboDatabases.SelectedIndex = 0;
    }
  22. Double-click the cboDatabases control and add the following code in the SelectedIndexChanged event handler:

  23. private void 
      cboDatabases_SelectedIndexChanged(
      object sender, System.EventArgs e)
    {
      switch (
       cboDatabases.SelectedItem.ToString())
      {
        case "Pubs":
        {
          // Instantiate the remote class
          dbc = (IDbConnect)
            Activator.GetObject(
            typeof(IDbConnect), 
            @"tcp://localhost:1234/Pubs.uri");
          break;
        }
        case "Northwind":
        {
          // Instantiate the remote class
          dbc = (IDbConnect)
            Activator.GetObject(
            typeof(IDbConnect), 
            @"tcp://localhost:1234/Northwind.uri");
          break;
        }
      }
    }
  24. Double-click the btnExecute control and add the following code in the Click event handler:

  25. private void btnExecute_Click
      (object sender, System.EventArgs e)
    {
      try
      {  
        // Invoke a method on 
        // the remote object
        this.dgResults.DataSource = 
          dbc.ExecuteQuery(
          this.txtQuery.Text);
        dgResults.DataMember = "Results";   
      }
      catch(Exception ex)
      {
        MessageBox.Show(ex.Message, 
          "Query Execution Error");
      }
    }
  26. Build the project. You now have a remoting client ready to use.

  27. Set the Exercise3_2_Server, the remoting server, as the startup project. Select Debug, Start Without Debugging to run the project. You should see a command window displaying a message that the server is started in the singleton activation mode. You should also see messages that the remote object is already created.

  28. Set Exercise3_2_Client, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Select a desired database from the combo box, enter a query in the text box, and click the button. The code invokes a method on the remote object that is already created on the server. The code binds the results from the remote method to the DataGrid control.

  29. Again select Debug, Start Without Debugging to run one more instance of the remoting client. Select a different database from the combo box, enter a query in the text box, and click the button. You should see that the second instance of the client fetches the data from the selected database.

Review Questions

  1. What is an application domain? How does the CLR manage an application domain?

  2. What are MBR objects? What are their advantages and disadvantages?

  3. What do you mean by a channel? What are the different types of channels the .NET Framework provides?

  4. What are the advantages and disadvantanges of the binary and SOAP formatters?

  5. What are the two modes for creating Server-Activated objects?

  6. When should you choose to create a client- activated object?

  7. What is the benefit of using declarative configuration over programmatic configuration?

  8. What are the two methods of the Activator class that enable you to create instances of remote objects?

  9. What are the advantages of using an IIS server as an activation agent?

  10. What should you do while creating a remotable class so that its methods can be called asynchronously?

Exam Questions

  1. You are designing a distributed application that hosts a remote object. You want only the authorized client applications to activate the remote object. You want to write the application with the minimum amount of code. Which of the following channels enables you to achieve this objective? (Select two.)

    1. HttpChannel

    2. HttpServerChannel

    3. TcpChannel

    4. TcpServerChannel

  2. You are designing a company-wide order processing system. This application is hosted on a server in the company's headquarters in Redmond, Washington, and is accessed by 1500 franchise locations throughout the world. The application specification mentions that the franchisees should be able to access the order-processing system even through firewalls. A large number of franchisees access the application over a slow connection and your objective is to maximize the performance of the application. Which of the following combinations of channel and formatter would you choose in this scenario?

    1. Use a TCP channel with a binary formatter.

    2. Use a TCP channel with a SOAP formatter.

    3. Use an HTTP channel with a binary formatter.

    4. Use an HTTP channel with a SOAP formatter.

  3. You are designing a distributed application for a large automotive company. The application allows the part suppliers across the globe to collect the latest design specifications for a part. The application is heavily accessed by the suppliers. For greater scalability, you are required to design the application so that it can be deployed in a load-balanced environment. How should you host the remotable object in this scenario?

    1. As a server-activated object in SingleCall activation mode.

    2. As a server-activated object in Singleton activation mode.

    3. As a client-activated object using the HTTP channel.

    4. As a client-activated object using the SOAP formatter.

  4. You have been hired by Great Widgets Inc., to create an application that enables their suppliers to access the purchase order information in real time. You create the required classes that the suppliers can activate remotely and package them into a file named gwpoinfo.dll. You plan to use IIS as the remoting host for this file. After the application has been deployed, minimal steps should be involved in changing the remoting configuration for this application. Any config-uration changes made to the purchase order information system should not affect any other applications running on that server. Which of the following files would you choose to configure remoting for the purchase order information system?

    1. gwpoinfo.dll

    2. web.config

    3. global.asax

    4. machine.config

  5. You have designed a remotable class named ProductDesign. You now want to register this class with the remoting system in such a way that client programs should be able to remotely instantiate objects of this class and invoke methods on it. You want to have only one instance of this class on the server, no matter how many clients connect to it. Which of the following code segments fulfills your requirement?

    1. RemotingConfiguration.
        RegisterWellKnownServiceType(
        typeof(ProductDesign), 
        "ProductDesign",    
        WellKnownObjectMode.SingleCall 
      );
    2. RemotingConfiguration.
        RegisterWellKnownServiceType(
        typeof(ProductDesign), 
        "ProductDesign",    
        WellKnownObjectMode.Singleton 
      );
    3. RemotingConfiguration.
        RegisterActivatedServiceType(
        typeof(ProductDesign), 
        "ProductDesign"    
      );
    4. RemotingConfiguration.
        RegisterWellKnownClientType(
        typeof(ProductDesign), 
        "ProductDesign"
      );
  6. You have designed a remotable class that allows the user to retrieve the latest weather information for their region. You do not want to write a lot of code to create a custom remoting host, so you decide to use IIS to host the application. The name of the remotable class is RemotingWeather.WeatherInfo and it is stored in an assembly named WeatherInfo.dll. You want the users to access this remotable class by using the URL http://RemoteWeather.com/users/WeatherInfo.rem. Which of the following remoting configurations would you place in the web.config file so that client applications can correctly retrieve weather information?
    1. <system.runtime.remoting>
        <application>
          <service>
            <activated type=
             "RemotingWeather.WeatherInfo, WeatherInfo"
            />
         </service>
         <channels>
            <channel ref="http" port="80" />
         </channels>
        </application>
      </system.runtime.remoting>
    2. <system.runtime.remoting>
        <application>
          <service>
            <wellknown mode="Singleton" 
             type=
             "RemotingWeather.WeatherInfo, WeatherInfo" 
             objectUri="WeatherInfo.rem"
            />
         </service>
        </application>
      </system.runtime.remoting>  
    3. <system.runtime.remoting>
        <application>
          <service>
            <activated type=
             "RemotingWeather.WeatherInfo, WeatherInfo"
            />
         </service>
         <channels>
            <channel ref="http server" 
              port="80" />
         </channels>
        </application>
      </system.runtime.remoting>
    4. <system.runtime.remoting>
        <application>
          <client>
            <wellknown mode="Singleton" 
             type=
             "RemotingWeather.WeatherInfo, WeatherInfo" 
             objectUri="WeatherInfo.rem"
            />
         </client>
        </application>
      </system.runtime.remoting>  
  7. You are a software developer for LubriSol, Inc., which manufactures chemicals for automobile industries. Your company does a lot of business with ReverseGear, Inc., which is the largest manufacturer of heavy vehicles in the country. ReverseGear, Inc., uses a .NET remoting application that allows its suppliers to check its daily parts requirements. Your objective is to create a client application to the ReverseGear's application that retrieves the information for parts produced by your company. All you know about the server application is its URL, which is http://ReverseGearInc.com/Suppliers/Req.rem. You want the quickest solution. What should you do to write a client application successfully?

    1. Contact ReverseGear, Inc., to ask for the interface and include reference to the interface in the client project.

    2. Open the URL in the Web browser and select View, Source to find how the remote class is structured.

    3. Use the Visual Studio .NET Add Web Reference feature to add a reference to the remote class in the client project.

    4. Use the Soapsuds tool to automatically generate the metadata and include the reference to this metadata in the client project.

  8. You want to host a remotable class via the .NET remoting framework so that remote clients can instantiate the class and invoke methods on it. The remotable class does not have any user interface but it must use Integrated Windows authentication to authenticate the users. Which of the following techniques you should use to host the remotable class? You want a solution that requires you to write minimal code.

    1. Use a console application as a remoting host.

    2. Create a Windows service and use that to host the remotable class.

    3. Use a Windows forms application to host the remotable class.

    4. Use Internet Information Services (IIS) as a remoting host.

  9. You are developing a remoting client to access a server-activated remotable object hosted at the URL tcp://finance:1234/Budget. You have obtained an interface assembly of this remote object. This assembly contains an interface named IBudget that is implemented by the remote class. You want to instantiate the remote object to invoke a method name GetDepartmentBudget(), which accepts a string value and returns a double value containing the department budget. Given the following code, what should you write in line 06 to successfully invoke the GetDepartmentBudget() method?

    01: // Register a TCP client channel 
    02: TcpClientChannel channel = 
       new TcpClientChannel();
    03: ChannelServices.RegisterChannel(
       channel);
    04: IBudget budget;
    05: // Instantiate the remote class
    06:
    07: // Invoke the remote method
    08: double budgetValue = 
       budget.GetDepartmentBudget("HR");
    1. budget = (IBudget)
          Activator.GetObject(typeof(IBudget),
          @"tcp://finance:1234/Budget");
    2. budget = (IBudget)
          Activator.CreateInstance(
           typeof(IBudget),
           @"tcp://finance:1234/Budget");
    3. budget = new IBudget();
    4. RemotingConfiguration.
      RegisterWellKnownClientType(
          typeof(IBudget), 
          @"tcp://finance:1234/Budget");
      Budget = new IBudget();
  10. You are developing an application that allows the client programs to instantiate a class named Inventory. You want the remote object to be created on the server so that it can access the inventory database. However, you want client programs to control the creation and the lifetime of the remote objects. Which of the following methods of the RemotingConfiguration class would you choose to register the remotable class with the remoting system on the server?

    1. RegisterWellKnownServiceType()

    2. RegisterWellKnownClientType()

    3. RegisterActivatedServiceType()

    4. RegisterActivatedClientType()

  11. You work for a large chemical manufacturing company that has four production units across the country. Your team has the responsibility of designing a distributed application that allows different production units to share and update material safety information for various products. One of your co-workers is using the following code to create a remoting host to host a server-activated object. She is getting an error. What should she do to resolve this error?

    01: using System.Runtime.Remoting;
    02: using System.Runtime.Remoting.Channels;
    03: using System.Runtime.Remoting.Channels.Tcp;
    04: using 
       System.Runtime.Remoting.Channels.Http;
    05: [STAThread]
    06: static void Main(string[] args)
    07: {
    08:  TcpServerChannel tcpChannel = 
           new TcpServerChannel(7777);
    09:  HttpServerChannel httpChannel = 
           new HttpServerChannel(8888);
    10:  RemotingConfiguration.
         RegisterWellKnownServiceType
         (typeof(MsdsInfo), "MsdsInfo", 
         WellKnownObjectMode.Singleton);
    11: }
    1. Remove the statement at line 09.

    2. Add the following statements just before line 10:

      ChannelServices.RegisterChannel(tcpChannel);
      ChannelServices.RegisterChannel(httpChannel);

    3. In the statement at line 08, replace TcpServerChannel with TcpChannel, and similarly in the statement in line 09, replace HttpServerChannel with HttpChannel.

    4. Use the same port numbers in the statements in line 08 and line 09.

  12. One of your coworkers has written the following code as part of a client application that activates a remote object. She is complaining that her program is not compiling. What should she modify in the program to remove this error? (Select all that apply.)

    01: DbConnect CreateObject()
    02: {
    03:   TcpClientChannel channel = 
          new TcpClientChannel(1234);
    04:   ChannelServices.RegisterChannel(
          channel);
    05:   RemotingConfiguration.
        RegisterWellKnownClientType(
    06:     typeof(DbConnect), 
    07:     "tcp://localhost/DbConnect" 
    08:     );
    09:   dbc = new DbConnect();
    10:   return dbc;
    11: }
    1. Change line 05 to use the RegisterWellKnownServiceType() method instead of the RegisterWellKnownClientType() method.

    2. Change the URL in line 07 to "tcp:// localhost:1234/DbConnect"

    3. Remove the port number from the constructor of TcpClientChannel() in line 03

    4. Change the code in line 07 to objectUri="DbConnect"

  13. The Soapsuds tool (soapsuds.exe) can be used to automatically generate the interface assembly for the remotable object. Which of the following statements are FALSE related to the Soapsuds tool? (Select two options.)

    1. The Soapsuds tool can be used to generate metadata for server-activated objects.

    2. The Soapsuds tool can be used to generate metadata for client-activated objects.

    3. The Soapsuds tool can be used to generate metadata for remotable objects registered through the HTTP channel.

    4. The Soapsuds tool can be used to generate metadata for remotable objects registered through the TCP channel.

  14. You have designed a Windows application that is used by the shipping department of a large distribution house. The Windows application instantiates a remotable class hosted on Internet Information Services (IIS). The remotable class provides various services to the Windows application, such as address validation and calculation of shipping rates. When you deploy the application, users complain that when they click the Validate Address button, the Windows application freezes and they can't take further action till the address has been verified. What should you do to improve the application's responsiveness?

    1. Use the binary formatter instead of the SOAP formatter.

    2. Use the TCP channel to communicate instead of the HTTP channel.

    3. Modify the remotable class to support asynchronous method calls.

    4. Modify the Windows application to call the methods asynchronously on the remote object.

  15. When you derive a class from the MarshalByRefObject to make the class remotable, which of the following members of the class are not remoted? (Select all that apply.)

    1. non-static public methods

    2. static methods

    3. non-static private methods

    4. non-static public properties

Answers to Review Questions

  1. The application domain, represented by the AppDomain class, is the basic unit of isolation for running applications in the CLR. The CLR allows several application domains to run within a single Windows process. The CLR ensures that code running in one application domain cannot affect other application domains. The CLR can terminate an application domain without stopping the entire process.

  2. MBR objects are remotable objects that derive from the System.MarshalByRefObject class. The MBR objects always reside on the server; the client application domain holds only a reference to the MBR objects and uses a proxy object to interact with the MBR objects. They are best suited when the remotable objects are large or when the functionality of the remotable objects is available only in the server environment on which it is created. However, they increase the number of network roundtrips between the server application domain and the client application domain.

  3. A channel is an object that transports messages across remoting boundaries such as application domains, processes, and computers. The .NET Framework provides implementations for HTTP and TCP channels.

  4. The binary formatter represents messages in a compact and efficient way, whereas the SOAP formatters are very verbose. The SOAP formatters can be used to communicate in heterogeneous environments, whereas the binary formatters can be understood only by .NET applications.

  5. The two modes for creating SAOs are SingleCall and Singleton. SingleCall activation mode creates a remote object for each client request. The object is discarded as soon as the request completes. Singleton activation mode creates a remote object only once and all the clients share the same copy. Hence SingleCall SAOs are stateless and Singleton SAOs maintain state global to all clients.

  6. CAOs are created for each client whenever the client requests. Hence CAOs are best suited when clients want to maintain private sessions with remote objects, when the clients want to control the lifetimes of the objects, or when they want to create customized remote objects with non-default properties.

  7. The main benefit of using declarative configuration over programmatic configuration is that you need not recompile the application after changing the remoting settings in the configuration file. The changes are picked up automatically.

  8. The GetObject() method (for server-activated objects) and CreateInstance() method (for client-activated objects) are the two methods of the Activator class that enable you to create instances of remote objects.

  9. Using IIS as an activation agent offers the following advantages:

    • You need not write a separate server program to register the remotable classes.

    • You need not worry about finding an available port for your server application. You can just host the remotable object and IIS automatically uses port 80.

    • IIS can provide other functionality, such as authentication and Secure Sockets Layer (SSL).

  10. A remotable object by default is capable of being called asynchronously; therefore no extra efforts are required to create remote objects that can be called asynchronously.

Answers to Exam Questions

  1. A, B. Your objective is to provide access to only authorized clients. Authorization is a function of the remoting host. IIS is the only available remoting host that provides you with this capability. IIS supports only HTTP communication. Therefore, you can use either the HttpChannel or HttpServerChannel channel because both allow you to listen to incoming messages from clients. IIS does not support TcpChannel and TcpServerChannel, so if you use these channels you have to write additional code to implement security and this is not desired in the given scenario. For more information, see the section "Choosing Between the HTTP and the TCP Channels" in this chapter.

  2. C. Firewalls generally allow HTTP messages to pass through, and the binary formatter provides a size-optimized format for encoding data. Using TCP may require administrators to open additional ports in the firewall, and the SOAP format is verbose when compared to binary and would take additional bandwidth, which is not a desirable solution for clients using slow connections. For more information, see the section "Choosing Between the HTTP and the TCP Channels" and "Channels and Formatters" in this chapter.

  3. A. Only server-activated objects in SingleCall activation mode support load-balancing because they do not maintain state across the method calls. For more information, see the section "Server-Activated Objects" in this chapter.

  4. B. You should store the remoting configuration in the web.config file. This file is an XML-based configuration file that is easy to modify and does not require a separate compilation step. Storing configuration settings in gwpoinfo.dll or global.asax requires the additional step of compilation before the settings come into effect. The machine.config file is not suggested because any changes made to it affect all the applications running on the server. For more information, see the section "Using Configuration Files to Configure the Remoting Framework" in this chapter.

  5. B. When you want to create just one instance of a remote object, without regard to the number of clients, you must create a server-activated object in the Singleton activation mode. For more information, see the section "Server-Activated Objects" in this chapter.

  6. B. IIS supports only well-known or server- activated objects. Therefore, you must use the <wellknown> element rather than the <activated> element. Also, you are specifying the configuration for the server, so you must use the <server> element rather than the <client> element inside the <application> element to configure the WellKnown object. For more information, see the section "Using Configuration Files to Configure the Remoting Framework" and "Using IIS As an Activation Agent" in this chapter.

  7. D. Because you know that the server's .NET remoting application is using HTTP, you can use the Soapsuds tool to automatically generate the metadata for the server. For more information, see the section "Using the Soapsuds Tool to Automatically Generate an Interface Assembly" in this chapter.

  8. D. You should use IIS as the remoting host because IIS has built-in support for Integrated Windows authentication. You'll have to write additional code to achieve this with other techniques. For more information, see the section "Using IIS As an Activation Agent" in this chapter.

  9. A. When you have an interface to a class and not the original class, you cannot use the new operator to instantiate the remote object. You should instead use the static methods of the Activator class. The Activator.GetObject() method is used to instantiate a server-activated object. For more information, see the section "Using Interface Assemblies to Compile Remoting Clients" in this chapter.

  10. C. Your requirement is to register a client-activated remote object on the server, so you'll use the RegisterActivatedServiceType() method. The RegisterActivatedClientType() method is used to register the CAO with the remoting system in the client's application domain. The other two options are for creating the server-activated objects. For more information, see the section "Creating a Client-Activated Object" in this chapter.

  11. B. In this program, although you have created an instance of TcpServerChannel and HttpServerChannel objects, you haven't yet registered them with the remoting framework. You'll register the channels by using the RegisterChannel() method of the ChannelServices class. For more information, see the sections "Using the SingleCall Activation Mode to Register a Remotable Class As a Server-Activated Object," "Using the Singleton Activation Mode to Register a Remotable Class As a Server-Activated Object," and "Registering a Remotable Class As a Client-Activated Object" in this chapter.

  12. B, C. When creating a client channel you should not specify a port number when calling the channel constructor. Instead, the port number should be used with the URL of the remote object. After the channels are created they should be registered with the remoting system. For more information, see the section "Channels" in this chapter.

  13. B, D. The Soapsuds tool is capable of generating metadata for server-activated objects on the HTTP channel. For more information, see the section "Using the Soapsuds Tool to Automatically Generate an Interface Assembly" in this chapter.

  14. D. The issue in the question is not of speed but of responsiveness. This behavior occurs because the Windows application is calling the methods on the remote object synchronously. You can make the user interface more responsive by simply calling the remote method asynchronously. No modifications are needed on the remotable object to achieve this behavior. For more information, see the section "Asynchronous Remoting" in this chapter.

  15. B, C. Only non-static public methods, properties, and fields participate in remoting. For more information, see the section "Remote Object Activation" in this chapter.

Suggested Readings and Resources

  1. Visual Studio .NET Combined Help Collection

    • .NET Remoting Overview

    • Remoting Examples

  2. Ingo Rammer. Advanced .NET Remoting. Apress, 2002.

  3. http://www.iana.org/assignments/port-numbers—List of assigned port numbers.

800 East 96th Street, Indianapolis, Indiana 46240

sale-70-410-exam    | Exam-200-125-pdf    | we-sale-70-410-exam    | hot-sale-70-410-exam    | Latest-exam-700-603-Dumps    | Dumps-98-363-exams-date    | Certs-200-125-date    | Dumps-300-075-exams-date    | hot-sale-book-C8010-726-book    | Hot-Sale-200-310-Exam    | Exam-Description-200-310-dumps?    | hot-sale-book-200-125-book    | Latest-Updated-300-209-Exam    | Dumps-210-260-exams-date    | Download-200-125-Exam-PDF    | Exam-Description-300-101-dumps    | Certs-300-101-date    | Hot-Sale-300-075-Exam    | Latest-exam-200-125-Dumps    | Exam-Description-200-125-dumps    | Latest-Updated-300-075-Exam    | hot-sale-book-210-260-book    | Dumps-200-901-exams-date    | Certs-200-901-date    | Latest-exam-1Z0-062-Dumps    | Hot-Sale-1Z0-062-Exam    | Certs-CSSLP-date    | 100%-Pass-70-383-Exams    | Latest-JN0-360-real-exam-questions    | 100%-Pass-4A0-100-Real-Exam-Questions    | Dumps-300-135-exams-date    | Passed-200-105-Tech-Exams    | Latest-Updated-200-310-Exam    | Download-300-070-Exam-PDF    | Hot-Sale-JN0-360-Exam    | 100%-Pass-JN0-360-Exams    | 100%-Pass-JN0-360-Real-Exam-Questions    | Dumps-JN0-360-exams-date    | Exam-Description-1Z0-876-dumps    | Latest-exam-1Z0-876-Dumps    | Dumps-HPE0-Y53-exams-date    | 2017-Latest-HPE0-Y53-Exam    | 100%-Pass-HPE0-Y53-Real-Exam-Questions    | Pass-4A0-100-Exam    | Latest-4A0-100-Questions    | Dumps-98-365-exams-date    | 2017-Latest-98-365-Exam    | 100%-Pass-VCS-254-Exams    | 2017-Latest-VCS-273-Exam    | Dumps-200-355-exams-date    | 2017-Latest-300-320-Exam    | Pass-300-101-Exam    | 100%-Pass-300-115-Exams    |
http://www.portvapes.co.uk/    | http://www.portvapes.co.uk/    |