MCAD/MCSD Training Guide (70-310): .NET Remoting
Date: Apr 4, 2003
Objectives
This Appendix Covers the following Microsoft-specified objective for the "Creating a Managing Microsoft Windows Services, Service Components, .NET Remoting Objects, and XML Web Services" section of the "Developing XML Web Services and Server Components with Microsoft Visual Basic .NET and the Microsoft .NET Framework" exam:
Create and consume a .NET Remoting object.
Implement server-activated components.
Implement client-activated components.
Select a channel protocol and a formatter. Channel protocols include TCP and HTTP. Formatters include SOAP and binary.
Create client configuration files and server configuration files.
Implement an asynchronous method.
Create the listener service.
Instantiate and invoke a .NET Remoting object.
This exam objective tests your skill in designing distributed applications using .NET remoting, which is part of the .NET Framework SDK. You should know how to create a remote object, how to make it available to users via a remoting server, and how to write a client application that instantiates remote objects and invokes their methods.
Remoting provides a very flexible and configurable environment for distributed applications. By virtue of this flexibility, .NET remoting applications can be designed in several different ways. In this exam, you are required to know how to make choices between various remoting configuration parameters (such as activation mode, channel, and formatter) for a given scenario.
Remoting is configurable. All the remoting settings can be written in an XML-based configuration file. Keeping these settings in separate configuration files allows easier modification and maintenance of remoting applications. This objective requires you to distinguish between several configuration files such as the application configuration file, the web.config file, and the machine.config file.
In distributed applications, methods are invoked across the network and several factors, such as network bandwidth and server availability, might cause your application to respond slowly as compared to a desktop application. This exam objective also tests your skill on creating asynchronous or responsive distributed applications.
Outline
- Introduction
- Application Boundaries
- Process Boundary
- Application Domain Boundary
- Distributed Applications
- Evolution of Distributed Applications
- Developing Distributed Applications Using the .NET Framework
- .NET Remoting Architecture
- Object Marshalling
- Marshal-by-Value Objects
- Marshal-by-Reference Objects
- Channels
- HTTP Channels
- TCP Channels
- Choosing Between the HTTP and the TCP Channels
- Formatters
- The SOAP Formatter
- The Binary Formatter
- Channels and Formatters
- Remote Object Activation
- Server-Activated Objects
- Client-Activated Objects
- Comparing the Object Activation Techniques
- Lifetime Leases
- Object Marshalling
- Applying .NET Remoting
- Creating a Remotable Class
- Creating a Server-Activated Object
- Registering a Remotable Class As a Server-Activated Object Using the SingleCall Activation Mode
- Instantiating and Invoking a Server-Activated Object
- Registering a Remotable Class As a Server-Activated Object Using the Singleton Activation Mode
- Creating a Client-Activated Object
- Registering a Remotable Class As a
- Client-Activated Object
- Instantiating and Invoking a Client-Activated Object
- Using Configuration Files to Configure the Remoting Framework
- Server-Side Configuration
- Client-Side Configuration
- Using Interface Assemblies to Compile Remoting Clients
- Creating an Interface Assembly
- Creating a Remotable Object That Implements an Interface
- Creating a Remoting Client That Uses an Interface Instead of the Implementation
- Using the Soapsuds Tool to Automatically Generate an Interface Assembly
- Creating an Interface Assembly That Works with the Client-Activated Objects
- Using IIS As an Activation Agent
- Asynchronous Remoting
- Understanding the Model of Asynchronous Programming in the .NET Framework
- Applying Asynchronous Programming
- Chapter Summary
- Apply Your Knowledge
Study Strategies
Write programs to create remotable objects, remoting hosts, and remoting clients. Understand what role each of these plays in a distributed computing scenario.
Understand the difference between server-activated objects and client-activated objects, HTTP channel and TCP channel, SOAP formatter and binary formatter. You should be ready to answer questions that ask you to choose between these remoting elements in a given scenario.
Use both declarative and programmatic configuration for distributed applications. Appreciate the advantages and shortcomings of each approach.
Understand how to make a client program responsive despite the delay in method calls across the network by using asynchronous programming techniques.
Introduction
The .NET Framework provides a platform for building next-generation distributed applications. I'll start this Appendix By introducing distributed applications 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'll discuss .NET remoting in this Appendix And cover ASP.NET Web services in the next two chapters.
In this chapter, you'll start learning about remoting by understanding its architecture. You'll learn about various remoting elements, such as remotable classes, remoting hosts, remoting clients, channels, formatters, and activation modes, and you'll also learn how these elements fit together to create a distributed application. I'll compare the various choices available with each of these 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'll first show you how to create a class that can be remoted across network and application boundaries. I'll 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'll also show how to create a client program that can instantiate a remote object and invoke methods on it.
I'll discuss various types of applications that can work as remoting host, such as a console application, a windows service, or IIS (Internet Information Services). I'll 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 in this chapter, I'll 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 the application boundaries supplied by 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 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
Each process has its own virtual address space, executable code, and data.
A Windows process cannot directly access the code or data of another Windows process.
A Windows process runs only one application, so if an application crashes, it does not affect other applications.
Process boundaries are a good thing because they allow processes to coexist. However, it takes 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 would involve several short-lived processes, which require 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 the .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. This is why code executed by the CLR is also known as managed code.
Unlike Windows, the CLR can verify the type-safety of programs to guarantee that a program does not request resources outside of its own boundary. The characteristics of the CLR help provide isolation between running programs at a lower cost than the process boundary.
Instead of a process, the basic unit of isolation for running applications in the CLR is an application domain. An application domain (also known as an AppDomain) is the smallest execution unit 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 provided by a Windows process.
The application domains achieve isolation through application domain boundaries, which ensure that
Each application domain contains its own set of code, data, and configuration settings.
An application domain cannot directly access the code or data structures of another application domain.
Code running in one application domain cannot affect other application domains. The CLR can terminate an application domain without stopping the entire process.
Creating, monitoring, and maintaining an application domain uses fewer resources than performing the same operations with a process. In addition, the capability of an application domain to run multiple applications within the same process reduces the overhead of process switching. Thus, application domains increase the performance of the applications.
You can create an application domain in a program using the AppDomain class of System namespace. However, in most cases the application domains are created and managed by the runtime hosts that 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 configuredthe Windows shell, ASP.NET, and Internet Explorer.
Distributed Applications
From the previous sections, you can see that both process and application domains provide a closely protected environment. As a result, objects in a process or an application domain cannot directly talk to objects in another process or 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:
Establishes communication between objects that run in different application domains and processes whether on the same computer or across the Internet.
Enables enterprise application integration by establishing communication between objects that run on heterogeneous architectures.
Enables application availability by making sure that portions of an application continue to run even if some components are busy or have failed.
Provides increased security and scalability by dividing the application into several layers (or tiers).
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 in which all components run on a single computer. This is a desirable model for an enterprise application.
Several efforts have been made 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 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 previously mentioned technologies 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, integration, or interoperability.
Developing Distributed Applications Using the .NET Framework
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):
The System.Net NamespaceThis namespace includes classes to create standalone listeners and custom protocol handlers to start from scratch, as well as to create your own framework for developing a distributed application. Working with the System.Net namespace directly requires a good understanding of network programming.
The System.Runtime.Remoting NamespaceThis namespace includes the classes that constitutes the .NET remoting framework. The .NET remoting framework enables communication between objects living in different application domains whether or not they are on the same computer. Remoting provides an abstraction over the complexities of network programming and exposes a simple mechanism for interapplication domain communication. The key objectives of .NET remoting are flexibility and extensibility.
The System.Web.Services NamespaceThis namespace includes the classes that constitute the ASP.NET Web services framework. ASP.NET Web services enable objects living in different application domains to exchange messages using standard protocols such as HTTP and SOAP. ASP.NET Web services, when compared to remoting, provide a much higher level of abstraction and simplicity. The key objectives of ASP.NET Web services are ease of use and interoperability with other systems.
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 instead of building a distributed programming framework from scratch using the System.Net namespace classes.
The functionality offered by .NET Remoting and ASP.NET Web services appears very similar to each other. In fact, ASP.NET Web services are actually built on the .NET Remoting infrastructure. It is also possible to design Web services using .NET remoting. Given the amount of similarity, how do you choose one over another in your project? Simply put, the decision depends on the type of application you want to create. You'll use
.NET Remoting when both of the endpoints (client and server) of a distributed application are in your control. This might be a case when an application has been designed for use within a corporate network.
ASP.NET Web services when one of the endpoints of a distributed application is not in your control. This might be a case when your application is interoperating with your business partner's application.
You will learn more about architectural differences and the specific features of both technologies as you progress through this book. I will discuss .NET remoting in this Appendix And I will discuss ASP.NET Web services in Chapter 4, "Basic Web Services," and Chapter 5, "Advanced Web Services."
REVIEW BREAK
An Application domain, or AppDomain, is the smallest execution unit for a .NET application. The CLR allows several application domains to run within a single Windows process.
Distributed Applications enable communication between objects that run in different application domains and processes.
The System.Net, System.Runtime.Remoting, and System.Web.Services namespaces enable the .NET Framework to support distributed application development.
.NET Remoting Architecture
.NET remoting enables objects in different application domains to talk to each other. The real strength of remoting is in enabling communication between objects when their application domains are separated across the network. In this case, remoting transparently handles details related to network communication.
Before getting into details, let's first answer a basic questionHow can remoting establish cross-application domain communication when an application domain does not allow direct calls across its boundary?
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 using the following steps:
When a client object wants to create 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.
Figure 3.1 In this simplified view of .NET remoting, note that the client and server communicate indirectly through a proxy object.
-
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.
-
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).
-
The remoting system on the server collects all the results of the method invocation and passes them through the channel to the remoting system on the client.
-
The remoting system at the client receives the response of the server and returns the results to the client object through the proxy.
The process of packaging and sending method calls among the objects across the application boundaries via serialization and deserialization, as shown in the preceding steps, is also known as marshalling.
Now that you have a basic idea of how .NET remoting works, its time to get into details. In the next few sections, I'll explain the various key components and terminology of .NET remoting.
Object Marshalling
Remotable objects are objects that can be marshalled 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 (MBV) ObjectsThese objects are copied and passed out of the server application domain to the client application domain.
Marshal-by-reference (MBR) ObjectsThese objects are accessed on the client side using a proxy. The client just holds a reference to these objects.
Marshal-by-Value Objects
MBV objects reside on the server. However, when the 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. The method is then invoked directly on the client. When this happens, the MBV object is no longer a remote object. Any method calls to the object do not require any proxy object or marshalling because the object is locally available.
The MBV objects can provide faster performance by reducing the number of network roundtrips, but in the case of large objects, the time taken to transfer the serialized object from the server to the client can be very significant. Further, MBV objects do not allow you the flexibility to run the remote object in the server environment.
A MBV object can be created by declaring a class with the Serializable attribute; for example
' Define a MBV remoting object <Serializable()> Public Class MyMBVObject ' Implementation details End Class
If a class needs to control its own serialization, it can do so by implementing the ISerializable interface as shown here:
Imports System.Runtime.Serialization ' Define a MBV remoting object <Serializable()> Public Class MyMBVObject Implements ISerializable ' Class details ... 'Implement custom serialization here Public Sub GetObjectData( _ ByVal info As SerializationInfo, _ ByVal context As StreamingContext) ' Serialization details ... End Sub End Class
Marshal-by-Reference Objects
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 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 good choice when the objects are prohibitively large or when the functionality of the object is only available in the server environment on which it is created.
An MBR object can be created by deriving from the System.MarshalByRefObject class; for example,
' Define a MBR remoting object Public Class MyMBRObject Inherits MarshalByRefObject ' Class details ... End Class
Channels
Create and consume a .NET Remoting object: Select a channel protocol and a formatter. Channel protocols include TCP and HTTP. Formatters include SOAP and binary.
Channels are the objects that transport messages across remoting boundaries such as application domains, processes, and computers. When a client calls a method on a remote object, the details of the method call 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 using the channel that best suits its requirements.
A channel has two endpoints. The channel object at the receiving end of a channel (the server) listens to a particular protocol using the specified port number, whereas the channel object at the sending end of the channel (the client) sends information to the receiving end 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 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 while the channel object at the sending end must implement the IChannelSender interface.
The .NET Framework provides implementations for HTTP (Hypertext Transfer 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
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 protocol to receive messages |
HttpClientChannel |
IChannelSender |
An implementation for a client channel that uses the HTTP protocol to send messages |
HttpChannel |
IchannelReceiver IChannelSender |
An implementation of a combined and 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:
Imports System Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http ' ... Dim channel As HttpChannel = 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
A 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 protocol to receive messages |
TcpClientChannel |
IChannelSender |
An implementation for a client channel that uses the TCP protocol to send messages |
TcpChannel |
IchannelReceiver IChannelSender |
An implementation of a combined and channel that provides the functionality for both the TcpServerChannel and TcpClientChannel classes |
The following code example shows how to register a sender-receiver TCP channel on port 1234:
Imports System Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp ' ... Dim channel As TcpChannel = New TcpChannel(1234) ChannelServices.RegisterChannel(channel) ' ...
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 unless you write your own security system.
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 limit the use of TCP channel to within a low-risk intranet. For more wide-reaching applications, using HTTP channel makes more sense unless the application's efficiency requirements justifies the cost of creating a customized security system.
Table 3.3 Choosing Between HTTP Channel and TCP Channel
Channel |
Scope |
Efficiency |
Security |
HttpChannel |
Wide, HTTP channel allows you to host objects in a robust HTTP server such as IIS. HTTP channels can be used over the Internet because Integrated firewalls do not generally block HTTP communication. |
Less, because HTTP is a bulky protocol and has a lot of extra overhead. |
More, because when remote objects are hosted in IIS, the HttpChannel can immediately take advantage of Secure Sockets Layer (SSL), Windows Authentication, or Kerberos. |
TcpChannel |
Narrow, Using TCP channel over the Internet would require 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
Formatters are the objects used to encode and serialize data into messages before they are transmitted over a channel.
To participate in the .NET remoting framework, a formatter class must implement the IFormatter interface. The .NET Framework packages two formatter classes for common scenariosthe 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 relatively straightforward, 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 incompatible architectures. However, SOAP is very verbose. SOAP messages require more bytes to represent data than the equivalent binary messages.
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 using SoapFormatter. You can create industry-standard XML Web services by using the 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 using BinaryFormatter.
However, channels are configurable. You can configure the HTTP channel to use the binary formatter or a custom formatter instead of the SOAP formatter. Similarly, the TCP channel can be configured to use the SOAP formatter or a custom formatter instead of the binary formatter.
Figure 3.2 compares the various combinations of channels and formatters on the scale of efficiency and compatibility. The protocols at the top of the list are the most efficient, while those at the bottom of the list are most interoperable. You can use this information to decide which combination of channel and formatter you would choose in a given scenario.
Figure 3.2 TCP channel with binary formatter provides maximum efficiency whereas HTTP channel with SOAP formatter provides maximum interoperability.
REVIEW BREAK
.NET remoting enables objects in different application domains to talk to each other even when they are separated by applications, computers, or the network.
The process of packaging and sending method calls among objects across the application boundaries via serialization and deserialization is called marshalling.
Marshal-by-value (MBV) and Marshal-by-reference (MBR) are the two types of remotable objects. MBV objects are copied to the client application domain from the server application domain, whereas only a reference to the MBR objects is maintained in the client application domain. A proxy object is created at the client side to interact with the MBR objects.
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 to enable communication of messages over the HTTP and TCP protocols, respectively.
A channel has two endpoints. A channel at the receiving end, the server, listens for messages at a specified port number from a specific protocol; and a channel object at the sending end, the client, sends messages through the specified protocol at the specified port number.
Formatters are the objects used to serialize and deserialize data into messages before they are transmitted over a channel. You can format messages in SOAP or the binary format with the help of the SoapFormatter and BinaryFormatter classes in the FCL.
The default formatter to transport messages to and from the remote objects for the HTTP channel is the SOAP formatter and for the TCP channel is the binary formatter.
Remote Object Activation
Between the two types of objects that you have seen (MBV objects and 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, an MBR object is classified into one of the following two categories:
Server-activated objects
Client-activated objects
Server-Activated Objects
Server-activated objects (SAO) 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.
Server-activated objects provide limited flexibility because they can only be instantiated using their default (parameter-less) constructors.
NOTE
Well-Known Objects Remote objects activated in SingleCall or Singleton activation mode are also known as well-known objects.
There are two possible activation modes for a server-activated object:
SingleCall activation mode
Singleton activation mode
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; therefore, they do not maintain state across requests. This behavior of SingleCall mode allows for greater server scalability as an object consumes server resources only for a small period, therefore allowing the server to allocate resources to other objects.
The SingleCall activation mode is a desired solution when
The overhead of creating an object is not significant.
The object is not required to maintain its state.
The server needs to support a large number of requests for the object.
The object needs to be supported in a load-balanced environment.
TIP
Load-Balancing and SingleCall Activation Sometimes to improve the overall efficiency of an application, it might be hosted on multiple servers that share the incoming requests to the application. 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 requests for such objects. For this reason, SingleCall activation is ideally suited for load-balanced environments.
Common scenarios of the SingleCall activation mode are those applications in which 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 are retrieving the inventory level for an item, displaying tracking information for a shipment, and so on.
Singleton Activation Mode
In the Singleton activation mode, at most there will be one instance of the remote object regardless of the number of clients accessing it.
A Singleton-mode object can maintain state information across method calls. For this reason, such objects are also sometimes known as stateful objects. The state maintained by the Singleton-mode object is globally shared by all of its clients. This generally means that you should not store any state in Singleton-mode objects. But there are circumstances (such as keeping track of usage statistics) in which it can make sense to store shared state.
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 later in the chapter.
A Singleton object is a desired solution when
The overhead of creating an object is substantial.
The object is required to maintain its state over a prolonged period.
Several clients need to work on the shared state.
Singleton activation mode is useful in scenarios such as in a chat server in which multiple clients talk to the same remote object and share data between one another through this object.
Client-Activated Objects
Client-activated objects (CAO) 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 the complete control over the lifetime of the objects.
Client-activated objects are instantiated on the server as soon as the client requests the object to be created. Unlike an SAO, a CAO does not delay the object creation until the first method is called on the object.
A CAO can be created using any of the available constructors for the class. A typical CAO activation involves the following steps:
When the client attempts to create an instance of the server object, an activation request message is sent to the remote server.
The server then creates an instance of the requested class 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.
The client uses the ObjRef object to create a proxy for the server object on the client side.
An instance of a CAO serves only the client 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 the Singleton SAO, different CAOs cannot share a common state.
The lifetime of a CAO is determined using lifetime leases. I'll talk more about this topic shortly in a section titled "Lifetime Leases."
A CAO is a desired solution when
The clients want to maintain a private session with the remote object.
The clients want to have more control over how the objects are created and how long they will live.
A CAO is useful in scenarios such as entering a complex purchase order in which 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.
The SingleCall server activation has maximum scalability because such objects occupy server resources for the minimum amount of the time. This enables the server to allocate its resources between a large numbers of 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.
Figure 3.3 The SingleCall server activation offers maximum scalability, whereas the client activation offers maximum flexibility.
Lifetime Leases
A lifetime lease is the period of time that a particular object can be active in memory before the .NET framework deletes it and reclaims its memory. Both Singleton SAO and CAO use lifetime leases to determine how long they should continue to exist.
TIP
Leases and Activation Mode Leases apply only to Singleton and Client Activated objects. In the SingleCall activation mode, objects are created and destroyed with each method call.
A lifetime lease is represented using an object that implements the ILease interface 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 |
Returns the amount of time remaining on the lease as a TimeSpan object. |
InitialLeaseTime |
Property |
Lets you supply a TimeSpan that dictates the default lifetime of the object. If the object does not receive any method calls, it will only live for this period. |
Register() |
Method |
Specifies an object to be notified when a lease needs to be renewed. |
Renew() |
Method |
Renews a lease for the specified TimeSpan. |
RenewOnCallTime |
Property |
Each time the remote object is called, the lease is renewed for this much time. |
SponsorshipTimeout |
Property |
Specifies the amount of time to wait for a sponsor object to respond to a renewal request. |
TIP
Custom and Infinite Lifetimes A remote object can choose to have a custom defined lifetime by overriding the InitializeLifetimeService() method of the base class, MarshalByRefObject. If the InitializeLifetimeService() method returns a null value, the type tells the .NET Remoting system that its instances are intended to have an infinite lifetime.
Simply speaking, the lease works as follows:
-
When an object is created, its lifetime lease (CurrentLeaseTime) is set using the value of the InitialLeaseTime property (which is 5 minutes by default).
-
Whenever the object receives a call, its CurrentLeaseTime is reset to the time specified by the value of the RenewOnCallTime property (which is 2 minutes by default).
-
The client can also renew a lease for a remote object by directly calling the ILease.Renew() method:
-
When the value of CurrentLeaseTime reaches 0, the .NET Framework contacts any sponsors registered with the lease to check if they are ready to sponsor renewing the object's lease.
-
If the sponsor does not renew the object or the server cannot contact the sponsor within the duration specified by the SponsorshipTimeout property, the object is marked for garbage collection.
Dim lease As ILease = _ CType(RemotingServices. _ GetLifetimeService(RemoteObject), ILease) Dim expireTime As TimeSpan = _ lease.Renew(TimeSpan.FromSeconds(60))
Sponsors are the objects responsible for dynamically renewing the lease of an object in case of its lease expiry. For more information about sponsors, refer to the "Renewing Leases" topic in the .NET Framework Developer's Guide.
REVIEW BREAK
MBR remotable objects can be activated in two modes: Server-activated mode and Client-activated mode.
Server-activated objects (SAO) are those remote objects whose lifetime is directly controlled by the server.
You can activate SAOs in two ways: SingleCall (object is created for each client request) and Singleton (object is created once on the server and is shared by all clients).
The SingleCall activation mode provides the maximum scalability because it does not maintain any state and the object lives for the shortest duration possible.
CAO are created for each client when the client requests to create a remote object. These objects maintain state for each client with which they are associated.
The leased-based lifetime process determines how long the Singleton SAO and CAO should exist.
Applying .NET Remoting
So far, I have discussed the architecture and various concepts related to the .NET remoting. In this section, you will learn how to apply these concepts to see remoting in action. In particular, you will learn how to
Create a remotable class.
Create a Server-activated object.
Create a Client-activated object.
Use configuration files to configure the remoting framework.
Use interface assemblies to compile remoting clients.
Creating a Remotable Class
Creating a remotable class is simple. All you need to do is to inherit a class from the MarshalByRefObject class. Step By Step 3.1 creates a remotable class named DbConnect. This class connects to a specified SQL Server database and allows you to execute a SELECT SQL statement using its ExecuteQuery() method.
STEP BY STEP 3.1: Creating a Remotable Class
-
Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 310C03. Click OK.
-
Add a new Visual Basic .NET Class library named StepByStep3-1 to the solution.
-
In the Solution Explorer, rename the default Class1.vb to DbConnect.vb.
-
Open the DbConnect.vb class and replace the code with the following code:
-
Select Build, Build StepByStep3-1. This step packages the remotable class into the file StepByStep3-1.dll, which is located in the bin or 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.
Imports System Imports System.Data Imports System.Data.SqlClient ' Marshal-by-Reference Remotable Object Public Class DbConnect Inherits MarshalByRefObject Private sqlconn As SqlConnection ' Default constructor connects to the Northwind ' database Public Sub New() sqlconn = New SqlConnection( _ "data source=(local);" & _ "initial catalog=Northwind;" & _ "integrated security=SSPI") Console.WriteLine( _ "Created a new connection " & _ "to the Northwind database") End Sub ' Parameterized constructor connects to the ' specified database Public Sub New(ByVal DbName As String) sqlconn = New SqlConnection( _ "data source=(local);" & _ "initial catalog=" & DbName & ";" & _ "integrated security=SSPI") Console.WriteLine( _ "Created a new connection " & _ "to the " & DbName & " database") End Sub Public Function ExecuteQuery( _ ByVal strQuery As String) As DataSet Console.Write("Starting to execute " & _ "the query...") ' Create a SqlCommand to represent the query Dim sqlcmd As SqlCommand = _ sqlconn.CreateCommand() sqlcmd.CommandType = CommandType.Text sqlcmd.CommandText = strQuery ' Create a SqlDataAdapter object ' to talk to the database Dim sqlda As SqlDataAdapter = _ New SqlDataAdapter() sqlda.SelectCommand = sqlcmd ' Create a DataSet to hold the results Dim ds As DataSet = New DataSet() Try ' Fill the DataSet sqlda.Fill(ds, "Results") Catch ex As Exception Console.WriteLine(ex.Message, _ "Error executing query") End Try Console.WriteLine("Done.") ExecuteQuery = ds End Function End Class
You've now created a remotable class, but it cannot yet be directly called from client application domains. 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
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 enables the design to be modular and the code to be reusable.
In this section, I'll show you how to create a remoting server. Here's an overview of the steps that the remoting server must take.
-
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 the channel with the remoting framework. This tells the framework which requests should be directed to this particular server. This registration is performed through the RegisterChannel() method of the ChannelServices class:
-
Register the remotable class with the remoting framework. This tells the framework which classes this particular server can create for remote clients. For a server-activated object, this registration is performed using the RegisterWellKnownServiceType() method of the RemotingConfiguration class, as shown here:
-
' Register a remote object with the remoting framework RemotingConfiguration.RegisterWellKnownServiceType( _ GetType(DbConnect), "DbConnect", _ WellKnownObjectMode.SingleCall)
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. The last parameter specifies the activation mode. The activation mode can be one of the two possible values of the WellKnownObjectMode enumerationSingleCall or Singleton.
' Register a TCP server channel on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ' Register a HTTP server channel on port Dim channel As HttpServerChannel = _ New HttpServerChannel(1234)
' Register the channel with remoting framework ChannelServices.RegisterChannel(channel)
TIP
Accessing an Object Through Multiple Channels From steps 2 and 3, you can 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.
-
With the channel and class both registered, the remoting server is ready to go. The remoting framework will direct all requests for that class via that channel to the registered server.
As I discussed, earlier, an SAO can be activated in two different modesSingleCall 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.
Registering a Remotable Class As a Server-Activated Object Using the SingleCall Activation Mode
In this section, I'll demonstrate how to create a server that exposes the remotable class through the remoting framework. The server process here will be a long running user interface-less process that will continue to listen for 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 a 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 server.
Later in this chapter, in the section "Using IIS As an Activation Agent," I'll cover how to use IIS as a remoting server. I'll talk about Windows services in Chapter 6, "Windows Services."
STEP BY STEP 3.2: Registering a Server-Activated Object Using the SingleCall Activation Mode
Add a new Visual Basic .NET Console application named StepByStep3-2 to the solution.
-
In the Solution Explorer, right-click the project StepByStep3-2 and select Add Reference from the context menu. In the Add Reference dialog box (see Figure 3.4), 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 (which 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 allows you to add references to components.
-
In the Solution Explorer, rename the default Module1.vb module to DbConnectSingleCallServer.vb. Open the file and change the name of the module to DbConnectSingleCallServer in the module declaration.
NOTE
Namespace Naming Note that although the project is named StepByStep3-1, the corresponding namespace is named StepByStep3_1. That's because the .NET Framework considers a dash to be an illegal character in a namespace name.
-
Add the following Imports directives above the module declaration:
-
Add the following code in the Main() procedure:
-
Right-click on the StepByStep3-2 project in the Solution Explorer and select Properties. Change the Startup object to DbConnectSingleCallServer.
-
Build the project. This step creates a remoting server that is capable of registering the StepByStep3_1.DbConnect class for remote invocation using the SingleCall activation mode.
Imports StepByStep3_1 Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp
Public Sub Main() ' Create and Register a TCP server channel ' that listens on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ChannelServices.RegisterChannel(channel) ' Register the service that publishes ' DbConnect for remote access in SingleCall mode RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(StepByStep3_1.DbConnect), "DbConnect", _ WellKnownObjectMode.SingleCall) Console.WriteLine("Started server in the " & _ "SingleCall mode") Console.WriteLine("Press <ENTER> to terminate " & _ "server...") Console.ReadLine() End Sub
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 HTTP channel is not difficultyou 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 might or might 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 on a particular computer 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 he starts 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 65535provided, of course, that the port number you choose is not used by any other internal application. In case you are distributing the application, you should get a port number registered with the 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. In order to achieve this, the remoting client needs to take the following steps:
-
Create and register a client channel that is used by the remoting framework to send messages to the remoting server. The type of the channel used by the client should be compatible with the channel used by server. The following examples show how to create a TCP client channel and an HTTP client channel:
' Create and register a TCP client channel Dim channel As TcpClientChannel = _ New TcpClientChannel() ChannelServices.RegisterChannel(channel) ' Create and register a HTTP client channel Dim channel As HttpClientChannel = _ 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 you register the remote class in the client's domain.
-
Register the remotable class as a valid type in the client's application domain. This registration is performed using the RegisterWellKnownClientType() method of the RemotingConfiguration class as shown here:
' Register the remote class as a valid ' type in the client's application domain RemotingConfiguration.RegisterWellKnownClientType( _ GetType(DbConnect), "tcp://localhost:1234/DbConnect")
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.
TIP
Instantiating a Server-Activated Object You can only instantiate an SAO at client side using its default constructor.
-
Instantiate the SAO on the server. You can only use the default constructor.
' Instantiate the remote object DbConnect dbc = new DbConnect()
I'll demonstrate the preceding steps in Step by Step 3.3. In this example, I'll 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
-
Add a new Visual Basic .NET Windows Application named StepByStep3-3 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the Startup object for the project.
-
Add the following directives to the form's module:
-
Place two GroupBox controls, a TextBox control (txtQuery), a Button control (btnExecute) and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Arrange the controls as shown in Figure 3.5.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports StepByStep3_1
Figure 3.5 This form invokes a method of a remote object to execute the given query.
-
Add the following code to the form directly after the designer-generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
Right-click on the name of the solution in the Solution Explorer window and select Properties. This opens the Solution Property Pages dialog box. In the dialog box, select the Multiple Startup Projects check box, 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 isn't already there, click the Move Up and Move Down buttons to get the right order.
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Register a TCP client channel Dim channel As TcpClientChannel = _ New TcpClientChannel() ChannelServices.RegisterChannel(channel) ' Register the remote class as a valid ' type in the client's application domain RemotingConfiguration. _ RegisterWellKnownClientType( _ GetType(DbConnect), _ "tcp://localhost:1234/DbConnect") ' Instantiate the remote class dbc = New DbConnect() End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
Figure 3.6 Use the Solution Property Pages dialog box to control which projects are started and in what order.
-
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.
-
Shortly afterward, 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.
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 that you can set the project you want to run first as the startup object and select Debug, Start Without Debugging to run the project. This won't lock Visual Studio .NET in the debug mode, and you'll be able to run more programs from within IDE using the same technique.
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, you'll note the output as shown in Figure 3.8. You'll note that the remote object is created every time you click on the Execute Query button. That's normal behavior based on what you've learned 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:
The first call to constructor is made by the remoting framework at the server side to check whether it is okay to call this object remotely and to check the activation mode of the object.
The second constructor is called because of the client's call on the remote object. In the case of SingleCall activation, the server does not preserve the state of the constructor that was called earlier; therefore, the object has to be re-created with each client request.
You might wonder why I included a reference to StepByStep3-1.dll in this project. You might support your argument by saying that the DbConnect class contained in the StepByStep3-1.dll is a remotable class and its right place is on the server and not on the client.
Well said, but I have included StepByStep3-1.dll because of the following reasons:
The client project StepByStep3-3 won't compile without itThe reason is that I am referring to the DbConnect class in the project StepByStep3-3, and the project StepByStep3-3 by itself has no definition of DbConnect. When I include a reference to StepByStep3-1.dll, the project StepByStep3-3 can resolve the definition for DbConnect from there and allow me to compile the project.
The client program StepByStep3-3.exe won't execute without itI can't remove StepByStep3-1.dll from the project directory after the compilation is successfully completed. The StepByStep3-1.dll is required again at the time of running the client. This is because to create the proxy object for the DbConnect class, the CLR must have the metadata that describes DbConnect. This metadata is read from the assembly stored in StepByStep3-1.dll.
In some cases, this isn't a good real-life solution because the StepByStep3-1.dll might 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.
Registering a Remotable Class As a Server-Activated Object Using the Singleton Activation Mode
The same remotable object can be activated in different modes without making any changes to the remotable object itself. In case of SAO, 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 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: Registering a Server-Activated Object Using the Singleton Activation Mode
-
Add a new Visual Basic .NET Console application named StepByStep3-4 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, rename the default Module1.vb to DbConnectSingletonServer.vb. Open the file and change the name of the Module to DbConnectSingletonServer in the module declaration.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Right-click on the StepByStep3-4 project in the Solution Explorer and select Properties. Change the Startup object to DbConnectSingletonServer.
-
Build the project. This step creates a remoting server capable of registering the StepByStep3_1.DbConnect class for remote invocation using the Singleton activation mode.
-
Set the 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 mode.
-
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.
Imports StepByStep3_1 Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp
Public Sub Main() ' Create and Register a TCP server channel ' that listens on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ChannelServices.RegisterChannel(channel) ' Register the service that publishes ' DbConnect for remote access in SingleCall mode RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(StepByStep3_1.DbConnect), "DbConnect", _ WellKnownObjectMode.Singleton) Console.WriteLine("Started server in the " & _ "Singleton mode") Console.WriteLine("Press <ENTER> to terminate " & _ "server...") Console.ReadLine() End Sub
Although there has been no change in the output for the client, if you note the messages generated by server (see Figure 3.9), you'll 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 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 establish communication between a remoting server and client using HTTP and SOAP?
This exercise helps you practice creating a remoting server and clients using the HTTP channels and SOAP formatter.
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:
-
Add a new Visual Basic .NET Console application named GuidedPracticeExercise3-1_Server to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, rename the default Module1.vb to DbConnectSingletonServer.vb. Open the file and change the name of the class to DbConnectSingletonServer in the class declaration.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Right-click on the GuidedPracticeExercise3-1 Server project in the Solution Explorer and select Properties. Change the startup object to DbConnectSingletonServer.
-
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 using the Singleton activation mode via the HTTP channel.
-
Add a new Visual Basic .NET Windows Application named GuidedPracticeExercise3-1_Client to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the startup object for the project.
-
Add the following directives to the form's module:
-
Place two GroupBox controls, a TextBox control (txtQuery), a Button control (btnExecute) and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.5 for the design of the form.
-
Add the following code to the form directly after the designer-generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
Build the solution. 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 mode. The remoting server is now ready to receive SOAP messages via HTTP.
-
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.
Imports StepByStep3_1 Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http
Sub Main() ' Create and Register a HTTP server channel ' that listens on port Dim channel As HttpServerChannel = _ New HttpServerChannel(1234) ChannelServices.RegisterChannel(channel) ' Register the service that publishes ' DbConnect for remote access in Singleton mode RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(StepByStep3_1.DbConnect), "DbConnect", _ WellKnownObjectMode.Singleton) Console.WriteLine("Started server in the " & _ "Singleton mode") Console.WriteLine("Press <ENTER> to terminate " & _ "server...") Console.ReadLine() End Sub
Imports StepByStep3_1 Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Register a TCP client channel Dim channel As HttpClientChannel = _ New HttpClientChannel() ChannelServices.RegisterChannel(channel) ' Register the remote class as a valid ' type in the client's application domain RemotingConfiguration. _ RegisterWellKnownClientType( _ GetType(DbConnect), _ "http://localhost:1234/DbConnect") ' Instantiate the remote class dbc = New DbConnect() End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
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 through Step By Step 3.4. After doing that review, try this exercise again.
Creating a Client-Activated Object
When exposing a remotable class as a CAO, no changes are required to be made 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:
-
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 the channel with the remoting framework. This registration is performed through the RegisterChannel() method of the ChannelServices class:
-
Register the remotable class with the remoting framework. For a client-activated object, this registration is performed using the RegisterActivatedServiceType() method of the RemotingConfiguration class as shown here:
' Register a TCP server channel on port Dim channel As TcpServerChannel = _ new TcpServerChannel(1234) ' Register a HTTP server channel on port Dim channel As HttpServerChannel = _ new HttpServerChannel(1234)
' Register the channel with remoting framework ChannelServices.RegisterChannel(channel);
' Register a remote object as CAO ' with the remoting framework RemotingConfiguration.RegisterActivatedServiceType( _ GetType(DbConnect), "DbConnect"))Here, the first parameter is the type of the remotable class. The second parameter specifies the URI through which the server publishes the location of the remote object.
Step By Step 3.5 shows how to expose the now-familiar DbConnect class from Step By Step 3.1 as a CAO.
STEP BY STEP 3.5: Registering a Remotable Class As a Client-Activated Object
-
Add a new Visual Basic .NET Console application named StepByStep3-5 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, rename the default Module1.vb to DbConnectCAOServer.vb. Open the file and change the name of the module to DbConnectCAOServer in the module declaration.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Right-click on the StepByStep3-5 project in the Solution Explorer and select Properties. Change the Startup object to DbConnectCAOServer.
-
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 using the client activation mode.
Imports StepByStep3_1 Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp
Sub Main() ' Create and Register a TCP Channel on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ChannelServices.RegisterChannel(channel) ' Register the client activated object RemotingConfiguration. _ RegisterActivatedServiceType( _ GetType(DbConnect)) Console.WriteLine( _ "Started server in the Client Activation mode") Console.WriteLine( _ "Press <ENTER> to terminate server...") Console.ReadLine() End Sub
Instantiating and Invoking a Client-Activated Object
To instantiate and invoke a client-activated object, the remoting client needs to take the following steps:
-
Create and register a client channel 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:
-
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 here:
' Create and register a TCP client channel Dim channel As TcpClientChannel = _ New TcpClientChannel() ChannelServices.RegisterChannel(channel) ' Create and register a HTTP client channel Dim channel As HttpClientChannel = _ New HttpClientChannel() ChannelServices.RegisterChannel(channel)
' Register DbConnect as a type on client, ' which can be activated on the server RemotingConfiguration.RegisterActivatedClientType( _ GetType(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 using any of its available constructors.
-
Instantiate the CAO on the server using the desired constructor.
' Instantiate the remote object Dim dbc As DbConnect = new DbConnect("Pubs")
I'll demonstrate the preceding steps in Step By Step 3.6. This example is similar to the client program created in Step By Step 3.3, but this time the client allows users to choose between the databases on the server.
STEP BY STEP 3.6: Instantiating and Invoking a Client-Activated Object
-
Add a new Visual Basic .NET Windows Application named StepByStep3-6 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the startup object for the project.
-
Add the following directives:
-
Place three GroupBox controls (grpDatabases, grpQuery and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery), 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.
-
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 names of databases in the editor:
Northwind Pubs
Click OK to add the databases to the Items collection of the cboDatabases control.
-
Add the following code just after the Windows form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the btnSelect control and add the following code in the Click event handler:
-
Double-click the btnExecute control and add the following code in the Click event handler:
-
Build the project. You now have a remoting client ready to use.
-
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.
-
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.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports StepByStep3_1
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load cboDatabases.SelectedIndex = grpQuery.Enabled = False End Sub
Private Sub btnSelect_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSelect.Click ' Disable the Databases group box and ' Enable the Query group box grpDatabases.Enabled = False grpQuery.Enabled = True ' Register a TCP client channel Dim channel As TcpClientChannel _ = 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( _ GetType(DbConnect), "tcp://localhost:1234") ' Instantiate the remote class dbc = New DbConnect( _ cboDatabases.SelectedItem.ToString()) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
Figure 3.10 A CAO client allows database selection by taking advantage of the capability to call various constructors.
-
Now, 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. Now switch to the Server command window. You 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 remotable object for each client.
Figure 3.11 The client-activated remoting server creates a new instance of the remote object for each client.
REVIEW BREAK
To create a remotable class, inherit the remotable class from the MarshalByRefObject class.
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.
You should create a server channel that listens on a given port number and register the channel with the remoting framework before you register the remotable class.
The type of channel registered by the client must be compatible with the type of channel used by the server for receiving messages.
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 domain.
To register SAO objects on the server side, you call the RemotingConfiguration.RegisterWellKnownServiceType() method; and to register SAO objects on the client side, you call the RemotingConfiguration.RegisterWellKnownClientType() method.
To register CAO objects on the server side, you call the RemotingConfiguration.RegisterActivatedServiceType() method; and to register CAO objects on the client side, you call the RemotingConfiguration.RegisterActivatedClientType() method.
You can only instantiate SAO objects at the client side using their default constructors, whereas you can instantiate CAO using any of the object's constructors.
Using Configuration Files to Configure the Remoting Framework
In all the examples so far, I have written code to register the channel and remote object with the remoting framework. This approach to specifying settings is also known as programmatic configuration. Although this approach works fine, there is a drawbackevery time you decide to make any change in how the channel or the remote objects are registered, you'll 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 in the code file. Any changes made to the configuration file can be automatically picked up by the program when it executes the next time. You need not recompile the sources. This approach of specifying configuration settings is also known as declarative configuration.
Declarative configuration can be specified at two levels:
Machine-LevelThe configuration settings at the machine-level can be specified through the machine.config file. The machine.config file is present in the CONFIG subdirectory of the .NET Framework installation (typically, Microsoft.NET\Framework\v1.0.3705\CONFIG in the Windows directory of your computer, if you're running version 1.0 of the .NET Framework). Any settings specified in this file apply to all the .NET applications running on the machine.
NOTE
Use Naming Convention for the Application-level Configuration Files Although it's not strictly 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 or security related, are all at one place.
Application-LevelThe configuration settings for a specific application can be specified through the application configuration file. In a Windows application, the name of the application-level configuration file includes the full application name and the extension, with .config appended to that extension. For example, the configuration file name for StepByStep3-1.exe is StepByStep3-1.exe.config. In an ASP.NET application, the name of the configuration file is web.config.
TIP
Application-Level Configuration File Takes Precedence over Machine-Level Configuration When you specify both application-level and machine-level configurations for an application, the application-level configuration takes priority over the machine-level configuration.
The general format of a configuration file is a 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 a --> <!-- a SAO while 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 --> <!-- a call to SAO while use the <activated> --> <!-- tag to configure a call to 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'll create a remoting server with Singleton activation mode similar to the one created in Step By Step 3.4. However, you'll note that the code itself is reduced because most of the configuration-related code is now moved to a separate configuration file.
STEP BY STEP 3.7: Registering a Server-Activated Object in the Singleton Activation Mode Using Configuration Files
-
Add a new Visual Basic .NET Console application named StepByStep3-7 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
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.
-
Open the StepByStep3-7.exe.config file and modify it to contain the following code:
-
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 folder under the project, where the StepByStep3-7.exe file will be created when the project is compiled.
-
In the Solution Explorer, rename the default Module1.vb to DbConnectSingletonServer.vb. Open the file and change the name of the module to DbConnectSingletonServer in the module declaration. Set the renamed module as the startup object for the project.
-
Add the following directive:
-
Add the following code in the Main() method:
-
Build the project. This step creates a remoting server capable of registering the StepByStep3-1.DbConnect class for remote invocation using the Singleton activation mode via its settings in the configuration file.
-
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 mode by configuring from its settings in the configuration file.
-
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.
<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>
Imports System.Runtime.Remoting
Sub Main() ' 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() End Sub
The most important thing to note in Step By Step 3.7 is the way I have written the <service> and the <channels> elements in the configuration file.
The <service> element is written as
<service> <!-- Set the activation 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 in which you specify the activation mode, type, and the object URI. The type attribute is specified as a pair of qualified class names (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 used by the server 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, andbased on the settingscalls the relevant methods to register the channels and the remoting objects.
Client-Side Configuration
The configuration of a remoting client is quite similar to that of a remoting server. However, you'll configure the <client> element of the configuration file instead of the <service> element.
Step By Step 3.8 demonstrates how to use client-side configuration files.
STEP BY STEP 3.8: Instantiating and Invoking a Server-Activated Object
-
Add a new Visual Basic .NET Windows Application named StepByStep3-8 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
In the Solution Explorer, right-click the 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.
-
Open the StepByStep3-8.exe.config file and modify it to contain the following code:
-
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 folder under the project, where the StepByStep3-8.exe file will be created when the project is compiled.
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the startup object for the project.
-
Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery), a Button control (btnExecute) and a DataGrid control (dgResults) on the form. Refer to Figure 3.5 for the design of this form.
-
Add the following directives:
-
Add the following code just after the Windows form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
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 mode by configuring from its settings in the configuration file.
-
Now, 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.
<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>
Imports System.Runtime.Remoting Imports StepByStep3_1
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Load remoting configuration RemotingConfiguration.Configure( _ "StepByStep3-8.exe.config") ' Instantiate the remote class dbc = New DbConnect() End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
The most important thing to note from this example 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.
<client> <!-- Set the remotable object and its URL --> <wellknown type= "StepByStep3_1.DbConnect, StepByStep3-1" url="tcp://localhost:1234/DbConnect" /> </client>
You can note that unlike the server configuration file, the <channel> element is not required for the client as it determines the protocol and the port number using the specified URL.
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 using an HTTP channel. You also want 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 the need to recompile the server.
How would you design such a remoting client and server?
This exercise helps you practice creating configuration files for both server and client that 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:
-
Add a new Visual Basic .NET Console application named GuidedPracticeExercise3-2_Server to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3_1 (the remotable class assembly).
-
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.
-
Open the GuidedPracticeExercise3-2_Server.exe.config file and modify it to contain the following code:
-
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 folder under the project, where the GuidedPracticeExercise3-2_Server.exe file will be created when the project is compiled.
-
In the Solution Explorer, rename the default Module1.vb to DbConnectCAOServer.vb. Open the file and change the name of the module to DbConnectCAOServer in the module declaration. Set the module as the Startup object for the project.
-
Add the following directive:
-
Add the following code in the Main() method:
-
Build the project. This step creates a remoting server capable of registering the StepByStep3_1.DbConnect class for remote invocation using the client activation mode via its settings in the configuration file.
-
Add a new Visual Basic .NET Windows Application named GuidedPracticeExercise3-2_Client to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-1 (the remotable class assembly).
-
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.
-
Open the GuidedPracticeExercise3-2_Client.exe.config file and modify it to contain the following code:
-
In the Solution Explorer, select the project and click the Show All Files button in the toolbar. 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.
-
In the Solution Explorer, remove the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the startup object for the project.
-
Place three GroupBox controls (grpDatabases, grpQuery and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery), two Button controls (btnSelect and btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of the txtQuery control to True. Refer to Figure 3.10 for the design of this form.
-
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 names of databases in the editor:
Northwind Pubs
Click OK to add the databases to the Items collection of the cboDatabases control.
-
Add the following directives to the form's module:
-
Add the following code directly after the Windows Forms designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the btnSelect control and add the following code in the Click event handler:
-
Double-click the btnExecute control and add the following code in the Click event handler:
-
Build the project. Set the 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 configuring from its settings in the configuration file.
-
Now, set GuidedPracticeExercise3-2_Client, the remoting client as the startup project. Select Debug, Start Without Debugging to run the project. Select a 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, which is created from the settings stored in the configuration file. The code binds the results from the remote method to the DataGrid control.
<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>
Imports System.Runtime.Remoting
Sub Main() ' 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() End Sub
<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>
Imports System.Runtime.Remoting Imports StepByStep3_1
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load cboDatabases.SelectedIndex = grpQuery.Enabled = False End Sub
Private Sub btnSelect_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSelect.Click 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()) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
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, you had to copy the assembly containing the remotable class to the client project (which happens automatically when you set a reference to the assembly) in order for the client to work. However, this is not a desirable solution in many cases because you might 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 only share 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'll demonstrate how you can create interfaces that will allow you to create a remote SAO and CAO without sharing the implementation. I'll 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'll create an assembly that implements an interface named IDbConnect class. The interface defines a contract. All the classes or structs that implement the interface must adhere to this contract.
STEP BY STEP 3.9: Creating an Interface Assembly
-
Add a new Visual Basic .NET Class library named StepByStep3-9 to the solution.
-
In the Solution Explorer, rename the default Class1.vbs to IDbConnect.vb.
-
Open the IDbConnect.vb and replace the code with the following code:
-
Build the project. This step creates an assembly that contains the definition of the IDbConnect interface.
Imports System Imports System.Data Public Interface IDbConnect Function ExecuteQuery(ByVal strQuery As String) _ As DataSet End Interface
Creating a Remotable Object That Implements an Interface
Now that you created the interface IDbConnect, you'll 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 that you must implement the method ExecuteQuery() and that the data type of parameters and the return value must match exactly with that defined in the interface.
Step By Step 3.10 creates the class DbConnect that adheres to the IDbConnect interface.
STEP BY STEP 3.10: Creating a Remotable Object That Implements an Interface Assembly
-
Add a new Visual Basic .NET Class library named StepByStep3-10 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-9 (the interface assembly).
-
In the Solution Explorer, rename the default Class1.vbs to DbConnect.vb.
-
Open the DbConnect.vb and replace the code with the following code:
-
Build the project. This step creates StepByStep3_10.DbConnect, a remotable class that implements the IDbConnect interface.
Imports System Imports System.Data Imports System.Data.SqlClient Imports StepByStep3_9 ' Marshal-by-Reference Remotable Object Public Class DbConnect Inherits MarshalByRefObject ' Implement the IDbConnect interface Implements IDbConnect Private sqlconn As SqlConnection ' Default constructor connects to the Northwind ' database Public Sub New() sqlconn = New SqlConnection( _ "data source=(local);" & _ "initial catalog=Northwind;" & _ "integrated security=SSPI") Console.WriteLine( _ "Created a new connection " & _ "to the Northwind database") End Sub ' Parameterized constructor connects to the ' specified database Public Sub New(ByVal DbName As String) sqlconn = New SqlConnection( _ "data source=(local);" & _ "initial catalog=" & DbName & ";" & _ "integrated security=SSPI") Console.WriteLine( _ "Created a new connection " & _ "to the " & DbName & " database") End Sub Public Function ExecuteQuery( _ ByVal strQuery As String) As DataSet _ Implements IDbConnect.ExecuteQuery Console.Write("Starting to execute " & _ "the query...") ' Create a SqlCommand to represent the query Dim sqlcmd As SqlCommand = _ sqlconn.CreateCommand() sqlcmd.CommandType = CommandType.Text sqlcmd.CommandText = strQuery ' Create a SqlDataAdapter object ' to talk to the database Dim sqlda As SqlDataAdapter = _ New SqlDataAdapter() sqlda.SelectCommand = sqlcmd ' Create a DataSet to hold the results Dim ds As DataSet = New DataSet() Try ' Fill the DataSet sqlda.Fill(ds, "Results") Catch ex As Exception Console.WriteLine(ex.Message, _ "Error executing query") End Try Console.WriteLine("Done.") ExecuteQuery = ds End Function End CLass
This program is similar to the one created in Step By Step 3.4, with the difference being that the DbConnect class is also implementing the IDbConnect interface in addition to deriving from the MarshalByRefObject class:
' Marshal-by-Reference Remotable Object Public Class DbConnect Inherits MarshalByRefObject ' Implement the IDbConnect interface Implements IDbConnect
You can expose this remotable object to the clients via the remoting framework using the techniques 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
-
Add a new Visual Basic .NET Console application named StepByStep3-11 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting, the project StepByStep3-9 (the interface assembly), and StepByStep3-10 (the remotable class assembly).
-
In the Solution Explorer, rename the default Module1.vb to DbConnectSingletonServer.vb. Open the file and change the name of the module to DbConnectSingletonServer in the module declaration. Set the module to be the Startup object of the project.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Build the project. This step creates a remoting server capable of registering StepByStep3_10.DbConnect, the remotable object that implements the StepByStep3_9.IDbConnect interface for remote invocation using the Singleton activation mode.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp
Sub Main() ' Register a TCP server channel that ' listens on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ChannelServices.RegisterChannel(channel) ' Register the service that publishes ' DbConnect for remote access in Singleton mode RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(StepByStep3_10.DbConnect), "DbConnect", _ WellKnownObjectMode.Singleton) Console.WriteLine("Started server in the " & _ "Singleton mode") Console.WriteLine("Press <ENTER> to terminate " & _ "server...") Console.ReadLine() End Sub
Creating a Remoting Client That Uses an Interface Instead of the Implementation
When the remotable class is implementing an interface, you can just include the reference to the interface assembly instead of the implementation assembly at the client side. The client will then extract 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
-
Add a new Visual Basic .NET Windows Application named StepByStep3-12 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-9 (the interface assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb, and set it as the startup object for the project.
-
Add the following directives:
-
Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.5 for the design of this form.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
Build the project. Set the 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 mode.
-
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.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports StepByStep3_9
' Declare a Remote object Dim dbc As IDbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Register a TCP client channel Dim channel As TcpClientChannel = _ New TcpClientChannel() ChannelServices.RegisterChannel(channel) ' Instantiate the remote class dbc = CType( _ Activator.GetObject(GetType(IDbConnect), _ "tcp://localhost:1234/DbConnect"), IDbConnect) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
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. This is by using the methods of Activator class as described here:
The Activator.GetObject() MethodCalls the proxy to send messages to the remote object. No messages are sent over the network until a method is called on the proxy. This method is useful for activating server-activated objects.
The Activator.CreateInstance() MethodCreates an instance of the specified type using the constructor that best matches the specified parameters. This method is useful for activating client-activated objects.
In Step By Step 3.12, I use the Activator.GetObject() method to create a proxy for the server-activated object indicated by the type IDbConnect and the specified server URL.
Using the Soapsuds Tool to Automatically Generate an Interface Assembly
In the previous section, you learned how you can distribute interface assemblies to the client instead of the implementation assembly. Clients will still be able to instantiate the remote objects in this case.
However, in case of large numbers of clients, distribution of interface files to each of them can become 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. So all the client needs to know is the URL of the remotable object, and they can generate the interface assembly on their own by using the soapsuds tool. A typical usage of the soapsuds tool is
soapsuds NoWrappedProxy -url:http://MyServer.com/DbConnect?wsdl -outputAssemblyFile:DbConnectInterface.dll
In this command line, the url switch specifies the URL of the remote object. You normally have to append ?wsdl to the URL to allow soapsuds to generate the metadata from the URL. The outputAssemblyFile switch specifies the name of the file in which you want the output assembly to be created. The NoWrappedProxy (or nowp) switch instructs the soapsuds tool to generate an unwrapped proxy.
NOTE
Wrapped Proxies Wrapped proxies are useful when you need to quickly test a Web service because with the use of wrapped proxies, you need not have to write code for channel configuration and remote object registration. However, for more flexibility you should use unwrapped proxies.
By default, the soapsuds tool generates wrapped proxiesproxies 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 yourself, it's 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.
TIP
Soapsuds Tool and Channels The soapsuds tool can only be used 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.
Step By Step 3.13 creates a client that uses the soapsuds generated unwrapped proxy to connect with the HTTP server created in the Guided Practice Exercise 3.1.
STEP BY STEP 3.13: Using the Soapsuds Tool to Automatically Generate an Interface Assembly
-
Add a new Visual Basic .NET Windows Application to your solution. Name it StepByStep3-13.
-
Set the GuidedPracticeExercise3-1_Server as the startup project. Select Debug, Start Without Debugging to run the project.
-
Select Start, Programs, Microsoft Visual Studio .NET, Visual Studio .NET Tools, Visual Studio .NET Command Prompt to launch a .NET command prompt.
-
Make sure that the GuidedPracticeExercise3-1_Server is running. Navigate to the bin folder of 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:
-
In Visual Studio .NET, add references to the .NET assembly System.Runtime.Remoting and DbConnectInterface.dll (the interface assembly auto generated by the soapsuds.exe in step 4).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb and set it as the Startup object for the project.
-
Add the following directives:
-
Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.5 for the design of this form.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
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 mode.
-
Now, 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.
soapsuds -url:http://localhost:1234/DbConnect?wsdl -oa:DbConnectInterface.dll -nowp
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http Imports StepByStep3_1
' Declare a Remote object Dim dbc As DbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' This step is not required if you use ' a wrapped proxy ' Register a HTTP client channel Dim channel As HttpClientChannel = _ 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( _ GetType(DbConnect), _ "http://localhost:1234/DbConnect") ' Instantiate the remote class dbc = New DbConnect() End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
In Step By Step 3.13, you learned how to use the soapsuds tool to automatically generate the interface class for the remotable object.
TIP
Soapsuds and Client-Activated Objects You cannot use soapsuds with client-activated objects because the metadata generated by the soapsuds tool can only create remote objects using the default constructor.
Also, the interface class generated by the soapsuds tool allows you to directly use the name of remotable class. This means that you can access the remotable class directly in the client as if you have a direct reference to the remotable class at the client side. For this reason, I can use the RegisterWellKnownClientType() method of the RemotingConfiguration class to register the remote class on the client side instead of using the Activator.GetObject() method.
Creating an Interface Assembly That Works with the Client-Activated Objects
Neither of 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") is useful for CAO.
The problem lies with the constructors. CAO has capabilities of invoking even the non-default constructors of the remotable class. However, an interface cannot contain the declaration for a constructor as it does with a method or a property.
This common problem is generally solved using the following steps:
Create an interface and a 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 to be exposed to the clients.
Create an interface that declares as many different methods as there are constructors in the remotable class. Each of these methods is responsible for creating an object in a way defined by its corresponding constructor. This technique is also called an abstract factory pattern. The name contains the word "factory" because it allows you to create objects in different ways. Let's call this interface IDbConnectFactory.
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. Let's call this class DbConnectFactory.
Create a remoting server that registers the class created in step 3 (DbConnectFactory) as the remotable class.
Create a client that connects to the server and create an instance of the remotable class created in step 3 (DbConnectFactory).
Invoke the methods corresponding to a constructor on the remotable object created in step 5. The return value of the constructor will be a remotable object of type defined in step 1 (DbConnect).
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'll show you how to implement the preceding technique to create a client-activated object that enables the creation of objects using any of its available constructors.
I'll use the same remotable class that I defined in Step By Step 3.10 (DbConnect) and its interface (IDbConnect) that I defined in Step By Step 3.9. So I'll directly start with step 2 of the 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 Object
-
Add a new Visual Basic .NET Class library named StepByStep3-14 to the solution.
-
Add a reference to the project StepByStep3-9 (the interface assembly).
-
In the Solution Explorer, rename the default Class1.vb to IDbConnectFactory.vb.
-
Open the IDbConnectFactory.vb and replace the code with the following code:
-
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.
Imports System Imports System.Data Imports StepByStep3_9 Public Interface IDbConnectFactory Function CreateDbConnectInstance() As IDbConnect Function CreateDbConnectInstance( _ ByVal dbName As String) As IDbConnect End Interface
The next step is to create a class that implements the IDbConnectFactory interface and then exposes that class as a remotable class through the remoting framework. Step By Step 3.15 shows how to do so.
STEP BY STEP 3.15: Creating a Remoting Server That Exposes DbConnectFactory As a Remotable Class
-
Add a new Visual Basic .NET Console application named StepByStep3-15 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting, the projects StepByStep3-9 (the interface assembly containing IDbConnect), StepByStep3-14 (the new interface assembly containing the IDbConnectFactory), and StepByStep3-10 (the remotable class assembly).
-
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.vb. Open the DbConnectFactory.vb class and replace the code with the following code:
-
In the Solution Explorer, rename the default Module1.vb as DbConnectFactoryServer.vb. Open the file and change the name of the module to DbConnectFactoryServer in the module declaration. Set this module to be the Startup object of the project.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Build the project. This step creates a remoting server capable of registering StepByStep3_10.DbConnect, the remotable object that implements the StepByStep3_9.IDbConnect interface, for remote invocation using the Singleton activation mode.
Imports System Imports StepByStep3_9 Imports StepByStep3_10 Imports StepByStep3_14 Public Class DbConnectFactory Inherits MarshalByRefObject Implements IDbConnectFactory Public Function CreateDbConnectInstance() _ As IDbConnect Implements _ IDbConnectFactory.CreateDbConnectInstance Return New DbConnect() End Function Public Function CreateDbConnectInstance( _ ByVal dbName As String) As IDbConnect _ Implements IDbConnectFactory. _ CreateDbConnectInstance Return New DbConnect(dbName) End Function End Class
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp
Sub Main() ' Register a TCP server channel that ' listens on port Dim channel As TcpServerChannel = _ New TcpServerChannel(1234) ChannelServices.RegisterChannel(channel) RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(DbConnectFactory), _ "DbConnectFactory", _ WellKnownObjectMode.Singleton) Console.WriteLine("Started server in the " & _ "client activated mode") Console.WriteLine("Press <ENTER> to terminate " & _ "server...") Console.ReadLine() End Sub
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 are CAO because they are created only on the explicit request from the client.
Step By Step 3.16 is the final step for creating CAO using the abstract factory pattern.
STEP BY STEP 3.16: Instantiating and Invoking a Client-Activated Object Using the Abstract Factory Pattern
-
Add a new Visual Basic .NET Windows Application named StepByStep3-16 to the solution.
-
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).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form and name it DbConnectClient.vb. Set the new form as the startup object for the project.
-
Add the following directives:
-
Place three GroupBox controls (grpDatabases, grpQuery and grpResults), a ComboBox control (cboDatabases), a TextBox control (txtQuery), two Button controls (btnSelect and btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.10 for the design of this form.
-
Select the Items property of the cboDatabases control in the Properties window and click on the (...) button. This opens String Collection Editor dialog box. Enter the following names of databases in the editor:
Northwind Pubs
Click OK to add the databases to the Items collection of the cboDatabases control.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the btnSelect control and add the following code in the Click event handler:
-
Double-click the btnExecute control and add the following code in the Click event handler:
-
Build the project. This step creates a remoting client that is capable of activating DbConnect as a CAO.
-
Set the 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 the Client Activation mode.
-
Now, 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.
-
Now, 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 the remote object is instantiated twice with different databases. This shows that the client activation creates an instance of remotable object per client.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Tcp Imports StepByStep3_9 ' contains IDbConnect Imports StepByStep3_14 ' contains IDbConnectFactory
' Declare a Remote object Dim dbc As IDbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load cboDatabases.SelectedIndex = grpQuery.Enabled = False End Sub
Private Sub btnSelect_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSelect.Click ' Disable the Databases group box and ' Enable the Query group box grpDatabases.Enabled = False grpQuery.Enabled = True ' Register a TCP client channel Dim channel As TcpClientChannel = _ 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 Dim dbcf As IDbConnectFactory = _ CType(Activator.GetObject(GetType(IDbConnect), _ "tcp://localhost:1234/DbConnectFactory"), _ IDbConnectFactory) dbc = dbcf.CreateDbConnectInstance _ (cboDatabases.SelectedItem.ToString()) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
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 will be only one instance for all the clients; however, there will be one DbConnect object for each client.
The .NET Framework allows you to store remoting configuration details in an XML-based configuration file instead of the code file. This causes any changes in the configuration file to be automatically picked up, rather than recompiling the code files.
To configure remoting configuration details from configuration files, you should call the RemotingConfiguration.Configure() method and pass the name of the configuration file.
You can distribute the interface assembly to the client instead of the implementation assembly by creating an interface that defines the contract and exposes the member definitions to the client. The remotable class should implement this interface.
You can use the soapsuds tool to automatically generate the interface class for the remotable object instead of manually defining the interface. However, the soapsuds tool only works when the HTTP channel is used for communication.
To create an interface that allows creating client-activated remote objects, you should create an additional interface that declares as many different methods as there are constructors in the remotable class. Each of these methods should be able to create the remote object in a way defined by its corresponding constructor.
Using IIS As an Activation Agent
So far, in this chapter, you are hosting the remotable class by creating your own server that is a console application. A 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 (which is a built-in Windows service) as an activation agent for the server process. I'll talk about the former alternative in Chapter 6 and the latter in this section.
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 the port 80.
IIS can provide other functionality such as authentication and secure sockets layer (SSL).
TIP
IIS Does Not Support CAO When creating IIS hosted remote objects, you cannot specify constructor parameters. Therefore, activating CAO is not possible using IIS.
The following list specifies what you need to do in order to host a remotable class in IIS:
Place the assembly containing the remotable objects into the \bin directory of an IIS Web application or place the assembly in the GAC (Global Assembly Cache) on the IIS computer.
Configure the remoting settings by placing the <system.runtime.remoting> configuration section into the web.Config file for the Web application. Alternatively, you can write the configuration code in the Application_Start() method of the global.asax file in the same way you would register a remote object in an .exe host.
NOTE
Channels and IIS Activation IIS Activation only supports the HTTP channel. The default formatting is SOAP, but IIS also supports binary and other custom formatting.
You should not specify a channel. IIS already listens on port 80. Specifying a port for a channel causes exceptions to be thrown when new IIS worker processes are started.
The well-known object URIs must end with ".rem" or ".soap" because these are the two extensions, which are registered with both IIS (via the aspnet_isapi.dll) as well as the remoting system (in machine.config).
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
-
Add a new Empty Web Project named StepByStep3-17 to the solution.
-
Add references to the project StepByStep3-9 (the interface assembly) and StepByStep3-10 (the remotable object) by selecting Add Reference from the context menu.
-
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:
-
IIS is now hosting StepByStep3_10.DbConnect, the remotable class, as a server activated object using the Singleton activation mode.
<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>
If you note the objectUri of the SAO in the preceding example, it ends with the extension .rem.
Step By Step 3.18 demonstrates how to invoke a remote object hosted by IIS.
STEP BY STEP 3.18: Instantiating and Invoking an IIS-Hosted Remote Object
-
Add a new Visual Basic .NET Windows Application named StepByStep3-18 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting and the project StepByStep3-9 (the interface assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb. Set the new form as the startup object for the project.
-
Add the following directives:
-
Place two GroupBox controls, a TextBox control (txtQuery), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.5 for the design of this form.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
Build the project. This step creates the remoting client that can invoke an IIS-hosted remote object.
-
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.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http Imports StepByStep3_9 ' contains IDbConnect
' Declare a Remote object Dim dbc As IDbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Register a Http client channel Dim channel As HttpClientChannel = _ New HttpClientChannel() ChannelServices.RegisterChannel(channel) ' Instantiate the remote class dbc = CType( _ Activator.GetObject(GetType(IDbConnect), _ "http://localhost/StepByStep3-17/DbConnect.rem"), _ IDbConnect) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnExecute.Click Try ' Invoke a method on the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
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, no port number is specified in the URL, and the URL ends with .rem.
Asynchronous Remoting
So far, all the method invocations that you 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.
Synchronous calls can make the user interface very non-responsive if the client program is waiting for a long process to finish execution. This is especially true for remote method calls in which additional time is involved because the calls are made across the network.
NOTE
Asynchronous Method Calls For asynchronous method calls, the remote types need not explicitly support the asynchronous behavior. It is up to the caller to decide whether a particular remote call is asynchronous or not.
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 Function _ LongProcessDelegate(param As String) As String
So, if there is a method definition such as
Function LongProcess(Param As String) As String ... End Function
Then the LongProcessDelegate can hold references to the method like this:
Dim delLongProcess As LongProcessDelegate delLongProcess = New LongProcessDelegate( _ AddressOf LongProcess)
Note the use of the AddressOf operator in returning the location of the LongProcess function.
Once you have the delegate object available, you can use its BeginInvoke() method to call the LongProcess() method asynchronously, such as follows:
Dim ar As IAsyncResult = _ delLongProcess.BeginInvoke("Test", Nothing, Nothing)
The IAsync 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 will immediately come back to the next statement while the LongProcess() method might still be executing.
To return the value of an asynchronous method call, you can call the EndInvoke() method on the same delegate, such as
Dim result As String = delLongProcess.EndInvoke(ar)
However, it is important to know where to place the preceding method call because when you call EndInvoke() and, if the LongProcess() method has not yet completed execution, EndInvoke() will cause the current thread to wait for the completion of LongProcess(). A poor use of EndInvoke(), such as placing it as the very next statement after BeginInvoke(), can potentially cause an asynchronous method call to turn into a synchronous method call.
One of the alternatives to this problem is to use the IsCompleted property of the ISyncResult object to check if the method has completed the execution and call the EndInvoke() method only in such cases.
Dim result As String If (ar.IsCompleted) Then result = delLongProcess.EndInvoke(ar) End If
But regular polling of the ar.IsCompleted property requires additional work at the client side. In fact, there's a better way to do this in the form of the 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 EndInvoke() method inside the callback method to collect the result of method execution.
To implement a callback method with an asynchronous method invocation, you need to take the following steps in the client program:
Define a callback method that you want to execute when the remote method has finished execution.
Create an object of delegate type AsyncCallback to store the reference to the method created in step 1.
Create an instance of an object that can receive a remote call to a method.
Declare a delegate type capable of storing references of the remote method.
Using the object from step 3, create a new instance of the delegate declared in step 4 to refer to the remote method.
Call BeginInvoke on the delegate created in step 5, passing any arguments and the AsyncCallback object.
Wait for the server object to call your callback method when the method has completed.
Applying Asynchronous Programming
In the following sections, I'll create a set of three projects to demonstrate the use of callback methods for an asynchronous method call:
The Remotable ClassThe Remotable class exposes the remote methods that are called from the client program.
The Remote ServerI'll use IIS to host the remotable object.
The Client ProgramThe client program calls the remote method asynchronously.
I'll 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 five seconds to simulate a long process call.
STEP BY STEP 3.19: Creating a Remotable Class
-
Add a new Visual Basic .NET Class Library named StepByStep3-19 to the solution.
-
In the Solution Explorer, rename the default Class1.vb to RemotableClass.vb.
-
Open the RemotableClass.vb and replace the code with the following code:
-
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\ directory of your project.
Imports System Imports System.Threading Public Class RemotableClass Inherits MarshalByRefObject Public Function LongProcess(ByVal param As String) _ As String Thread.Sleep(5000) Return param End Function End Class
RemotableClass is now ready to be hosted in a remoting host. In Step By Step 3.20, I decided to host the remotable class using IIS because it requires only a minimal amount of code. I'll, of course, control the remoting configuration by modifying the web.config file.
STEP BY STEP 3.20: Using IIS to Host a Remotable Class
-
Add a new Visual Basic ASP.NET Web Application named StepByStep3-20 to the solution.
-
Add a reference to the project StepByStep3-19 (the remotable class assembly).
-
Open the web.config file and add the following <system.runtime.remoting> element inside the <configuration> element:
-
The remoting server is now hosting the RemotableClass contained in the assembly StepByStep3-19 for remote invocation using the Singleton activation mode.
<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>
Neither RemotableClass nor the remoting host contains any additional information that supports an asynchronous method call. The asynchronous call is completely managed in the client code. So in the final step, I'll create a client application that calls a remote method synchronously as well as asynchronously. Showing both ways of calling a method will help you to understand the difference.
STEP BY STEP 3.21: Instantiating and Invoking a Client That Calls Remote Object Methods Synchronously and Asynchronously
-
Add a new Visual Basic .NET Windows Application named StepByStep3-21 to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting.
-
Add a new class file named StepByStep3-19.vb.
-
Replace the code in the StepByStep3-19.vb file with code to define the interface and location of the remote object:
Imports System Imports System.Runtime.Remoting.Messaging Imports System.Runtime.Remoting.Metadata Imports System.Runtime.Remoting.Metadata.W3cXsd2001 <Serializable()> _ Public Class RemotableClass Inherits System.MarshalByRefObject <SoapMethod(SoapAction:= "http://schemas.microsoft.com/clr/nsassem/StepByStep3_19.RemotableClass /StepByStep3-19#LongProcess")> _ Public Function LongProcess(ByVal param As String) _ As String Return (CType(CType(Nothing, Object), String)) End Function End Class
NOTE
Generating the Proxy Class The soapsuds tool will generate source code for a proxy class. However, it will only generate this code in C#, not in VB .NET. The Step By Step 3.19 code was developed by running soapsuds url:http://localhost/StepByStep3-20/RemotableClass.rem?wsdl -gc -nowp and then manually translating the C# code to VB .NET.
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named SyncAsync.vb. Set this form as the startup object for the project.
-
Add the following directives:
-
Place 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.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the btnSync control and add the following code in the Click event handler:
-
Double-click the btnAsync control and add the following code in the Click event handler:
-
Add the following callback, LongProcessCompleted() method definition, to the form's code-behind file at the class level:
-
Build the project. This step creates a remoting client for the RemotableClass remotable object.
-
Set the StepByStep3_21, the remoting client, as the startup project. Select Debug, Start Without Debugging to run the project. Click the Call LongProcess 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 LongProcess 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 are able to still work with the application (such as moving the form, clicking buttons and so on, although clicking the synchronous call button freezes the application). When the method is completed, you are notified by appending the completed message in the text box as shown in Figure 3.12.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http
' Remotable object Dim remObject As RemotableClass ' Create a delegate for the LongProcess method ' of the Remotable object Delegate Function LongProcessDelegate( _ ByVal param As String) As String ' Declare a LongProcessDelegate ' object, an AsyncCallback ' delegate object and an IAsyncResult object Dim delLongProcess As LongProcessDelegate Dim ab As AsyncCallback Dim ar As IAsyncResult ' Declare an integer variable to hold ' number of times the method is called Dim counter As Integer
Private Sub SyncAsync_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Register a Http client channel Dim channel As HttpClientChannel = _ New HttpClientChannel() ChannelServices.RegisterChannel(channel) ' Instantiate the remote class remObject = CType(Activator.GetObject( _ GetType(RemotableClass), _ "http://localhost/StepByStep3-20/RemotableClass.rem"), _ RemotableClass) ' Create a AsyncCallback delegate object to hold ' the reference of the ' LongProcessCompleted method, which is ' called when the asynchronous call is completed ab = New AsyncCallback( _ AddressOf LongProcessCompleted) ' Create a LongProcessDelegate ' delegate object to hold ' the reference of the LongProcess method delLongProcess = New LongProcessDelegate( _ AddressOf remObject.LongProcess) End Sub
Private Sub btnSync_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSync.Click ' Increment the method call counter counter += Dim param As String = String.Format( _ "Call: {0}, Type=Synchronous", counter) ' Append the start message to the text box txtResults.AppendText(String.Format( _ "{0}, Started at: {1}" & vbCrLf, _ 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}" & _ vbCrLf, _ param, DateTime.Now.ToLongTimeString())) End Sub
Private Sub btnAsync_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnAsync.Click ' Increment the method call counter counter += Dim param As String = 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, Nothing) End Sub
Sub LongProcessCompleted(ByVal ar As IAsyncResult) ' Call the EndInvoke method to retrieve the return ' value of the asynchronous method call Dim result As String = delLongProcess.EndInvoke(ar) ' Append the completed message to the text box txtResults.AppendText( _ String.Format("{0}, Completed at: {1}\n", _ result, DateTime.Now.ToLongTimeString())) End Sub
Figure 3.12 The asynchronous method call invokes the method and transfers the control back to the form. The asynchronous call uses a callback method to get a notification when the remote method finishes the execution.
In this program, I followed 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."
REVIEW BREAK
You can choose to run the remoting host as a console application, as a Windows service, or as an IIS application.
You can use IIS as an activation agent only when the underlying communication is in HTTP channel. Using IIS as an activation agent eliminates the need to write a separate server program that listens on a unique port number (IIS uses the port 80).
When creating IIS hosted remote objects, you cannot specify constructor parameters; therefore, activating CAO is not possible using IIS.
You can invoke a method asynchronously by calling the BeginInvoke() method on the delegate of that method.
You can automatically get a notification when an asynchronous method ends. However, you must first create another delegate object (of AsyncCallback type) that refers to the callback method that you need to execute when the remote method ends. And then you should pass the delegate to the callback method as an argument to the BeginInvoke() method.
Chapter Summary
The .NET Framework is a modern platform for building distributed applications. In this chapter, you learned how to create distributed applications using the remoting library of the .NET Framework.
You learned that the remoting framework is made up of several elements such as a remotable class, remoting host, remoting client, channel, formatters, and so on. Each of these elements is configurable and extensible. You practiced creating a remotable class and hosting that in several modes such as server-activated SingleCall, server-activated Singleton, and client-activated objects. 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 remoting for a remoting host and a remoting client. You noted that depending on your requirements, you can save the remoting configuration at two levelsone at the application level (.exe.config, .dll.config, or web.config for IIS) and the other at machine-level (machine.config).
Finally, I discussed the technique of invoking a remote method asynchronously with the help of delegates. An asynchronous method class makes a user-interfacebased application, such as a Windows forms application, quite responsive to user input despite the delays caused in a remote method call.
Key Terms
Application domain
Asynchronous call
Channel
Marshalling
Proxy
Synchronous call
Remoting
Runtime Host
Apply Your Knowledge
Exercises
3.1 - Using HTTP Channels with Binary Formatters
As I discussed in the chapter, the default formatter for an HTTP channel is SOAP and for a 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.
-
Launch Visual Studio .NET. Select File, New, Blank Solution, and name the new solution 310C03Exercises. Click OK.
-
Add a new Empty Web Project named Exercise3-1_Server to the solution.
-
Add references to StepByStep3-9.dll (the interface assembly containing IDbConnect) and StepByStep3-10.dll (the remotable object, DbConnect) from the 310C03 solution. You'll need to use the Browse button in the Add Reference dialog box to locate these libraries.
-
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:
-
IIS is now hosting the StepByStep3-10.DbConnect, the remotable class, as a server activated object using the Singleton activation mode.
-
Add a new Visual Basic .NET Windows Application named Exercise3-1_Client to the solution.
-
Add a reference to StepByStep3_9.dll (the interface assembly containing IDbConnect).
-
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.
-
Open the Exercise3_1_Client.exe.config file and modify it to contain the following code:
-
In the Solution Explorer, select the project and click the Show All Files button in the toolbar. Move the Exercise3-1_Client.exe.config file from the project folder to the bin folder under the project, where the Exercise3-1_Client.exe file will be created when the project is compiled.
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb and set it as the startup object for the project.
-
Place two GroupBox controls (grpQuery and grpResults), a TextBox control (txtQuery), a Button control (btnExecute), and a DataGrid control (dgResults) on the form. Set the Multiline property of txtQuery to True. Refer to Figure 3.5 for the design of this form.
-
Add the following directives:
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the Button control and add the following code in the Click event handler:
-
Build the project. Set the 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 protocol. The code binds the results from the remote method to the DataGrid control.
<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>
<configuration> <system.runtime.remoting> <application> <channels> <channel ref="http"> <serverProviders> <formatter ref = "binary" /> </serverProviders> </channel> </channels> </application> </system.runtime.remoting> </configuration>
Imports System.Runtime.Remoting Imports StepByStep3_9
' Declare a Remote object Dim dbc As IDbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load ' Load remoting configuration RemotingConfiguration.Configure( _ "Exercise3-1_Client.exe.config") ' Instantiate the remote class dbc = CType( _ Activator.GetObject( _ GetType(IDbConnect), _ "http://localhost/Exercise3-1_Server/DbConnect.rem"), IDbConnect) End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnExecute.Click Try ' Invoke a method on ' the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
Note that you should always configure the client to use the binary formatter when configuring the server (IIS) to use the binary formatter. You can specify the desired formatter for the client and need not specify the channel 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 WellKnown Object
WellKnown objects cannot be invoked from a client with a non-default constructor. You can create an object using any constructor you wish, initialize it anyway you wish, and then make it available to clients.
Use RemotingServices.Marshal() to publish an existing object instance.
Estimated Time: 30 minutes.
-
Add a new Visual Basic .NET Console Application named Exercise3-2_Server to the solution.
-
Add references to .NET assembly System.Runtime.Remoting, the StepByStep3-9.dll (the interface assembly containing IDbConnect), and StepByStep3-10.dll (the remotable object, DbConnect).
-
In the Solution Explorer, rename the default Module1.vb to DbConnectServer.vb. Open the file and change the name of the module to DbConnectServer in the module declaration. Set this new module to be the Startup object for the project.
-
Add the following directives:
-
Add the following code in the Main() method:
-
Build the project. This step creates a remoting server that creates the remotable object StepByStep3_10.DbConnect and is capable of marshalling the remote object across application boundaries.
-
Add a new Visual Basic .NET Windows Application named Exercise3-2_Client to the solution.
-
Add references to the .NET assembly System.Runtime.Remoting, and the project StepByStep3-9 (the interface assembly).
-
In the Solution Explorer, delete the default Form1.vb. Add a new form named DbConnectClient.vb and set it as the startup object for the project.
-
Add the following directives:
-
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.
-
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 names of databases in the editor:
Northwind Pubs
Click OK to add the databases to the Items collection of the cboDatabases control.
-
Add the following code directly after the Windows Form designer generated code:
-
Double-click the form and add the following code in the Load event handler:
-
Double-click the cboDatabases control and add the following code in the SelectedIndexChanged event handler:
-
Double-click the btnExecute control and add the following code in the Click event handler:
-
Build the project. You now have a remoting client ready to use.
-
Set the Exercise3-2_Server, 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 Singleton mode. You should also see messages that the remote object is already created.
-
Now, 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.
-
Now, 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.
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting. _ Channels.Tcp Imports StepByStep3_10
Sub Main() ' Create and Register a ' TCP server channel ' that listens on port Dim channel As TcpServerChannel = _ 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 Dim dbcPubs As DbConnect = _ New DbConnect("Pubs") RemotingServices.Marshal( _ dbcPubs, "Pubs.uri") Dim dbcNorthwind As DbConnect = _ 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() End Sub
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting. _ Channels.Tcp Imports StepByStep3_9
' Declare a Remote object Dim dbc As IDbConnect
Private Sub DbConnectClient_Load( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load ' Register a TCP client channel Dim channel As TcpClientChannel = _ New TcpClientChannel() ChannelServices.RegisterChannel( _ channel) cboDatabases.SelectedIndex = End Sub
Private Sub _ cboDatabases_SelectedIndexChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cboDatabases.SelectedIndexChanged Select Case cboDatabases. _ SelectedItem.ToString() Case "Pubs" ' Instantiate the remote class dbc = CType( _ Activator.GetObject( _ GetType(IDbConnect), _ "tcp://localhost:1234/Pubs.uri"), IDbConnect) Case "Northwind" ' Instantiate the remote class dbc = CType( _ Activator.GetObject( _ GetType(IDbConnect), _ "tcp://localhost:1234/Northwind.uri"), _ IDbConnect) End Select End Sub
Private Sub btnExecute_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btnExecute.Click Try ' Invoke a method on ' the remote object Me.dgResults.DataSource = _ dbc.ExecuteQuery(Me.txtQuery.Text) dgResults.DataMember = "Results" Catch ex As Exception MessageBox.Show(ex.Message, _ "Query Execution Error") End Try End Sub
Review Questions
What is an application domain? How does the CLR manage an application domain?
What are MBR objects? What are their advantages?
What is a channel? What are the different types of channels provided by the .NET Framework?
What are the advantages and disadvantanges of the Binary and SOAP formatters?
What are the two modes to create Server-Activated objects?
When should you choose to create a Client-Activated object?
What is the benefit of using declarative configuration over programmatic configuration?
What are the two methods of the Activator class that allow you to create instances of remote objects?
What are the advantages of using IIS server as an activation agent?
What should you do while creating a remotable class so that its methods can be called asynchronously?
Exam Questions
You are designing a distributed application that hosts a remote object. You want only the authorized client application to be capable of activating the remote object. You want to write the application with a minimum amount of code. Which of the following channels enables you to achieve this objective? (Select two choices.)
HttpChannel
HttpServerChannel
TcpChannel
TcpServerChannel
You are designing a company-wide order processing system. This application is hosted on a server in the company's headquarters in Redmond, WA 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 the 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 channel and formatter combinations would you choose in this scenario?
Use a TCP channel with a binary formatter
Use a TCP channel with a SOAP formatter
Use an HTTP channel with a binary formatter
Use an HTTP channel with a SOAP formatter
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?
As a server-activated object in SingleCall activation mode
As a server-activated object in Singleton activation mode
As a client-activated object using the HTTP channel
As a client-activated object using the SOAP formatter
You have been hired by Great Widgets Inc. to create an application that allows their supplier to access the purchase order information in real time. You create the required classes that can be activated remotely by the suppliers and package them into a file named gwpoinfo.dll. You plan to host this file using IIS as the remoting host. Your goal is that after the application has been deployed, there should be minimal steps involved to change the remoting configuration for this application. Also, any configuration 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?
gwpoinfo.dll
web.config
global.asax
machine.config
You have designed a remotable class named ProductDesign. You now want to register this class with the remoting system in such a way that the client program should be able to remotely instantiate objects of this class and invoke methods on it. You want there to be only one instance of this class on the server irrespective of the number of clients connected to it. Which of the following code snippets fulfills your requirement?
-
RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(ProductDesign), _ "ProductDesign", _ WellKnownObjectMode.SingleCall)
RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(ProductDesign), _ "ProductDesign", _ WellKnownObjectMode.Singleton)
RemotingConfiguration. _ RegisterActivatedServiceType( _ GetType(ProductDesign), _ "ProductDesign")
RemotingConfiguration. _ RegisterWellKnownClientType( _ GetType(ProductDesign), _ "ProductDesign")
-
-
You have designed a remotable class that allows the user to retrieve the latest weather information for her region. You do not want to write a lot of code to create a custom remoting host, so you decide to host the application using IIS. 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 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?
<system.runtime.remoting> <application> <service> <activated type= "RemotingWeather.WeatherInfo, WeatherInfo" /> </service> <channels> <channel ref="http" port="80" /> </channels> </application> </system.runtime.remoting>
-
<system.runtime.remoting> <application> <service> <wellknown mode="Singleton" type= "RemotingWeather.WeatherInfo, WeatherInfo" objectUri="WeatherInfo.rem" /> </service> </application> </system.runtime.remoting>
-
<system.runtime.remoting> <application> <service> <activated type= "RemotingWeather.WeatherInfo, WeatherInfo" /> </service> <channels> <channel ref="http server" port="80" /> </channels> </application> </system.runtime.remoting>
-
<system.runtime.remoting> <application> <client> <wellknown mode="Singleton" type= "RemotingWeather.WeatherInfo, WeatherInfo" objectUri="WeatherInfo.rem" /> </client> </application> </system.runtime.remoting>
-
You are a software developer for LubriSol Inc., which manufactures chemicals for automobile industries. Your company does major 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 the daily parts requirements. Your objective is to create a client application to the ReverseGear, Inc.'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 in order to successfully write a client application?
Contact ReverseGear, Inc. to ask for the interface and include references to the interface in the client project.
Open the URL in the Web browser and select View, Source to find out how the remote class is structured.
Use the Visual Studio .NET Add Web Reference feature to add a reference to the remote class in the client project.
Use the soapsuds tool to automatically generate the metadata and include the reference to this metadata in the client project.
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 should you use to host the remotable class? You want a solution that requires you to write minimum code.
Use a console application as a remoting host.
Create a Windows service and use that to host the remotable class.
Use a Windows forms application to host the remotable class.
Use Internet Information Services (IIS) as a remoting host.
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 named GetDepartmentBudget() that accepts a string value and returns a double value containing the department budget. Given the following code, what should you write in line 08 in order to successfully invoke the GetDepartmentBudget() method? Line numbers are for reference only.
01: ' Register a TCP client channel 02: Dim channel As TcpClientChannel = _ 03: New TcpClientChannel() 04: ChannelServices. _ 05: RegisterChannel(channel) 06: Dim budget As IBudget 07: ' Instantiate the remote class 08: 09: ' Invoke the remote method 10: Dim budgetValue As Double = _ 11: budget.GetDepartmentBudget("HR")
-
budget = CType( _ Activator.GetObject(GetType(IBudget), _ "tcp://finance:1234/Budget"), IBudget)
-
budget = CType( _ Activator.CreateInstance( _ GetType (IBudget), _ "tcp://finance:1234/Budget"), IBudget)
-
budget = New IBudget()
-
RemotingConfiguration. _ RegisterWellKnownClientType( _ GetType (IBudget), _ "tcp://finance:1234/Budget") Budget = new IBudget()
-
You are developing an application that enables 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 have control of 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?
RegisterWellKnownServiceType()
RegisterWellKnownClientType()
RegisterActivatedServiceType()
RegisterActivatedClientType()
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 creating a remoting host to host a server-activated object using the following code, but she is getting an error. What should she do to resolve this error?
01: Imports System.Runtime.Remoting 02: Imports System.Runtime. _ 03: Remoting.Channels 04: Imports System.Runtime.Remoting. _ 05: Channels.Tcp 06: Imports System.Runtime.Remoting. _ 07: Channels.Http 08: 09: Sub Main() 10: ' Create and Register channels 11: Dim tcpChannel _ 12: As TcpServerChannel = _ 13: New TcpServerChannel(7777) 14: Dim httpChannel As _ 15: HttpServerChannel = _ 16: New HttpServerChannel(8888) 17: RemotingConfiguration. _ 18: RegisterWellKnownServiceType _ 19: (GetType(MsdsInfo), _ 20: "MsdsInfo", _ 21: WellKnownObjectMode.Singleton) 22: End Sub
Remove the statement in lines 14 through 16.
Add the following statements just before line 17:
ChannelServices.RegisterChannel(tcpChannel) ChannelServices.RegisterChannel( _ httpChannel)
In the statement in lines 11 through 13, replace TcpServerChannel with TcpChannel and similarly in the statement in lines 14 through 16, replace HttpServerChannel with HttpChannel.
Use same port numbers in the statements in line 13 and line 16.
One of your co-workers 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: Function CreateObject() As DbConnect 02: ' Create channel 03: Dim channel As TcpClientChannel = _ 04: new TcpClientChannel(1234) 05: ChannelServices.RegisterChannel( _ 06: channel) 07: RemotingConfiguration. _ 08: RegisterWellKnownClientType( _ 09: GetType(DbConnect), _ 10: "tcp://localhost/DbConnect") 11: 12: dbc = new DbConnect() 13: return dbc 14: End Function
Change line 8 to use the RegisterWellKnownServiceType() method instead of the RegisterWellKnownClientType() method.
Change the URL in line 10 to "tcp://localhost:1234/DbConnect".
Remove the port number from the constructor of TcpClientChannel() in line 4.
Change the code in line 10 to objectUri="DbConnect".
The soapsuds tool (soapsuds.exe) can be used to automatically generate the interface assembly for the remotable object. Which of the following statements related to the soapsuds tool are FALSE? (Select two options.)
The soapsuds tool can be used to generate metadata for server-activated objects.
The soapsuds tool can be used to generate metadata for client-activated objects.
The soapsuds tool can be used to generate metadata for remotable objects registered through the HTTP channel.
The soapsuds tool can be used to generate metadata for remotable objects registered through the TCP channel.
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 button named Validate Address, the windows application freezes and they can't take further actions until the address has been verified. What should you do to improve the responsiveness of the application?
Use the binary formatter instead of the SOAP formatter.
Use the TCP channel to communicate instead of the HTTP channel.
Modify the remotable class to support asynchronous method calls.
Modify the Windows application to call the methods asynchronously on the remote object.
When you derive a class from MarshalByRefObject to make the class remotable, which of the following members of the class are not remoted? (Select all that apply.)
Non-static public methods
Static methods
Non-static private methods
Non-static public properties
Answers to Review Questions
-
The application domain, or AppDomain, 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.
-
MBR objects are remotable objects that derive from the System.MarshalByRefObject class. MBR objects always reside on the server. The client application domain only holds a reference to MBR objects and uses a proxy object to interact with MBR objects. They are best suited when the remotable objects are large or when the functionality of the remotable objects is only available 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.
-
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.
-
The Binary formatter represents messages in a compact and efficient way, whereas the SOAP formatter is very verbose. The SOAP formatter can be used to communicate in heterogenous environments, whereas the binary formatter can be understood only by the .NET applications.
-
The two modes to create SAO 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.
-
CAO are created for each client whenever the client requests. Hence, CAOs are best suited when clients want to maintain private session with remote objects, when the clients want to control the lifetime of the objects, or when they want to create a customized remote object with non-default properties.
-
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.
-
The GetObject() (for server-activated objects) and CreateInstance() (for client-activated objects) methods are the two methods of Activator class that allow you to create instances of the remote objects.
-
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 using the port 80.
-
IIS can provide other functionalities such as authentication and secure socket layers (SSL).
-
-
A remotable object is capable of being called asynchronously by default; therefore, no extra efforts are required in order to create remote objects that can be called asynchronously.
Answers to Exam Questions
A and 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 only supports 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; therefore, if you use these channels, you'll have to write additional code to implement security, and this is not desired in the given scenario.
C. Firewalls generally allow HTTP messages to pass through, and the binary formatter provides a size-optimized format of encoding data. Using TCP might require administrators to open additional ports in the firewall, whereas the SOAP format is verbose when compared to TCP and would take bandwidth, which is not a desirable solution for clients using slow connections.
A. Only server-activated objects in SingleCall activation mode support load-balancing because they do not maintain state across the method calls.
B. You should store the remoting configuration in a 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 a configuration setting 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 done to it will affect all the applications running on the server.
B. When you want to create just one instance of a remote object irrespective of the number of clients, you must create a server-activated (WellKnown) object in the Singleton activation mode.
B. IIS only supports WellKnown or server-activated objects. Therefore, you must use the <WellKnown> element instead of the <activated> element. Also, you are specifying the configuration for the server; therefore, you must use the <server> element instead of the <client> element inside the <application> element to configure the WellKnown object.
D. Because you know that the server's .NET remoting application is using HTTP protocol, you can use the Soapsuds tool to automatically generate the metadata for the server.
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 the other techniques.
A. In a case in which you just 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, whereas Activator.CreateInstance() method is used to instantiate a client-activated object.
C. Your requirement is to register a client-activated remote object; therefore, 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.
B. In the preceding 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 using the RegisterChannel() method of the ChannelServices class.
B and C. When creating a client channel, you should not specify a port number with the channel constructor. Instead, the port number should be used with the URL of the remote object.
B and D. The soapsuds tool is capable of generating metadata for server-activated objects on the HTTP channel.
D. The issue in the question is not of speed but of responsiveness. This behavior is 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.
B and C. Only non-static public methods, properties, and fields participate in remoting.
Suggested Readings and Resources
-
Visual Studio .NET Combined Help Collection
-
.NET Remoting Overview
-
Remoting Examples
-
-
Ingo Rammer. Advanced .NET Remoting. Apress, 2002.
-
http://www.iana.org/assignments/port-numbersList of assigned port numbers.