Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
We introduce you to the benefits of building composite applications with the Composite Application Guidance for WPF from Microsoft patterns & practices.

By Glenn Block (September 2008)
ADO.NET Data Services provide Web-accessible endpoints that allow you to filter, sort, shape, and page data without having to build that functionality yourself.

By Shawn Wildermuth (September 2008)
See how routed events and routed commands in Windows Presentation Foundation form the basis for communication between the parts of your UI.

By Brian Noyes (September 2008)
Technology changes at a lightening-fast pace. This month Howard Dierking considers how the rapid changes affect developer priorities and magazine focus.

By Howard Dierking (September 2008)
More ...
Articles by this Author
This month Dino builds a service layer that authenticates users of Silverlight 2 and ASP.NET AJAX services to prevent illegal access to sensitive back-end services.

By Dino Esposito (September 2008)
Dino Esposito compares the use of AJAX patterns and DOM manipulations to the use of the ASP.NET partial rendering engine.

By Dino Esposito (August 2008)
In this installment, the author provides an enhanced implementation of the BST pattern and compares it to HTM solutions.

By Dino Esposito (July 2008)
AJAX is meant to go beyond mere partial page rendering. Find out where Dino Esposito thinks dynamic pages are headed in the future with ASP.NET AJAX.

By Dino Esposito (June 2008)
This month we begin a look at the Single Page Interface (SPI) model and some design patterns for designing AJAX applications.

By Dino Esposito (May 2008)
This month, use nested ListView controls to create hierarchical views of data and extend the eventing model of the ListView by deriving a custom ListView class.

By Dino Esposito (April 2008)
This month Dino Esposito shows you how to get Windows-style modal dialog boxes for your Web applications thanks to the Ajax Control Toolkit and some clever coding.

By Dino Esposito (Launch 2008)
This month Dino looks at AJAX control extenders again, adding more advanced features including masked editing and autocompletion.

By Dino Esposito (February 2008)
More ...
Popular Articles
Learn how to automate custom SharePoint application deployments, use the SharePoint API, and avoid the hassle of custom site definitions.

By E. Wilansky, P. Olszewski, and R. Sneddon (May 2008)
Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

By Kenny Kerr (May 2008)
See how to build a document-level Visual Studio Tools for Office customization and integrate it with a content type in SharePoint.

By Steve Fox (May 2008)
Microsoft Robotics Studio is not just for playing with robots. It also allows you to build service-based applications for a wide range of hardware devices.

By Sara Morgan (June 2008)
More ...
Read the Blog
SQL Server 2008 supports a new data type, HierarchyID, that helps solve some of the problems in modeling and querying hier­archical information. In the September 2008 issue of MSDN Magazine, Kent Tegels introduces you to the ...
Read more!
Many people using SharePoint technologies don't realize that there is auditing support built directly into the Windows SharePoint Services (WSS) 3.0 platform. In the September 2008 issue of MSDN Magazine, Ted Pattison walks you through a ...
Read more!
The September 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Hierarchy ID: Model ...
Read more!
Silverlight 2 features a rich and robust control model that is the basis for the controls included in the platform and for third-party control packages. You can also use this control model to build controls of your own. In the August 2008 issue of MSDN Magazine, Jeff Prosise describes how to ...
Read more!
In the August 2008 issue of MSDN Magazine, Matt Milner covers several topics regarding development with Windows Workflow Foundation, some that are intended to address specific reader questions, such as how to safely share a persistence database ...
Read more!
LINQ is a powerful tool enabling quick filtering data based on a standard query language. It can tear through a structured set of data using a simple and straightforward syntax. In the August 2008 issue of MSDN Magazine, Jared Parsons demonstrates a ...
Read more!
More ...
.NET Remoting
Design and Develop Seamless Distributed Applications for the Common Language Runtime
Dino Esposito
Code download available at: NetRemoting.exe (87 KB)
Browse the Code Online

This article assumes you're familiar with Visual Basic .NET
Level of Difficulty 1 2 3
SUMMARY
Prior to the advent of .NET, DCOM was the underlying technology for remote communications between Windows-based applications. But DCOM is quirky to set up and configure and not as interoperable as it should be. In .NET, XML Web Services and .NET Remoting are a seamless and effective answer to the demand for tools to build distributed applications.
This article provides a primer on .NET Remoting with insights into the internal plumbing. Important aspects of remoting, such as channels, object lifetime management, and clients for remote objects are discussed. In addition, some practical examples are provided.
Today, one way to differentiate application models is by how tightly coupled the client and the server applications are. Loosely coupled applications usually can get by with a thin client and rely on HTTP and other open standards for physical connection and transportation. Tightly coupled applications work according to a two-tier, client/server pattern and don't necessarily have to rely on universally available protocols. Often, a tight coupling is a real necessity and these apps can't just be rewritten to use stateless, disconnected technologies like HTTP and SOAP.
Before the Microsoft® .NET Framework, DCOM was the underlying technology of choice for remote communication between Windows®-based applications. But DCOM was based on a proprietary binary protocol and suffered from a number of ailments, including a quirky setup and configuration process.
In this article, you'll find an in-depth look at effective mechanism for designing distributed applications for the common language runtime (CLR). The two technologies I'll examine are XML Web Services and .NET Remoting. Both let you expose services over the network and handle incoming calls. Both share a common software substrate especially for object serialization, data transportation, and underlying network protocols. But they shouldn't be considered alternate approaches to the same problem. Rather, .NET Remoting and Web Services are distinct technologies with a different set of goals and built-in features. For illustrative purposes, think of Web Services as a subset of the .NET Remoting infrastructure. Web Services are targeted to cross-platform communication and heterogeneous systems. While .NET Remoting could be utilized for cross-platform communication, it is optimized for communication between .NET-centric applications. It also takes in the best and the coolest aspects of DCOM and elegantly fixes the holes.

A Short History of DCOM
As users began asking for distributed applications capable of interoperating with a wide variety of remote systems, the natural answer was to take COM and attach a logical wire to both of its ends. DCOM became the network extension of COM, building a new infrastructure on the same successful component technology. Seamless integration, a flat learning curve, and the facility to maximize investments in COM-based applications and tools were the most compelling reasons to use DCOM.
Because it was a binary protocol, DCOM performance was considered to be good, especially when compared to text-based interaction like those taking place over HTTP. DCOM applications were fundamentally location-independent since the protocol infrastructure was flexible in the way it covered physical distance. For example, DCOM automatically created a pair of proxy/stub modules for any interprocess and intermachine communication and resolved the call within the boundary of the current process whenever possible. So what were the drawbacks?
In many Internet situations, the level of connectivity allowed between a client and a server is subject to a variety of restrictions. Proxy servers can get in the way. Firewalls might filter incoming Internet requests to protect the server components from unauthorized contact. The ultimate effect of such restrictions is that a DCOM client and a server could set up and carry out a conversation only through a narrow set of protocol and port combinations. DCOM dynamically selected a network port in the range of 1024 through 65535. Unfortunately, the selected port might be unreachable because system administrators normally prohibit inbound Internet traffic from passing through these ports. The DCOM security model was based on the assumption that developers and administrators would properly configure the security settings for each component. DCOM was transparent with respect to security, hiding all the security requirements of a component.
Over the years, DCOM was extended to work around these issues. In particular, the COM Internet Services (CIS) layer gave DCOM the ability to work over port 80 thanks to the Tunneling Transmission Control Protocol (TTCP). CIS worked as an ISAPI filter, requiring Microsoft Internet Information Services (IIS) 4.0 or higher to be up and running on the server machine. Even with this firewall avoidance technique, problems still arose. Not all firewalls will accept binary packets over HTTP. CIS did allow DCOM to find a port to enter through, but it affected the way DCOM components actually work; server components couldn't call back the client component to sink events or send notifications.
The advent of the .NET Framework supplanted most COM-related technology, and DCOM is no exception. The .NET architecture for remoting is a complete redesign of the concepts behind DCOM with two key goals: allowing for seamless and location-independent coding and allowing a fully operational method of interaction with restricted servers.
The .NET Remoting system is like a coin with two faces. On one side, you've got XML Web Services—an interoperability technology that you should already know about (unless you've spent the past two years in a cave). The second face of remoting is the classes of the .NET Remoting namespace. These classes promote optimized, effective communication between .NET-based applications. That said, the power and flexibility of Remoting is evident when used for communication between .NET-based applications. If you are looking for interoperability, ASP.NET Web Services are ideal. But if you just need to communicate between two .NET-centric applications, nothing is better than .NET Remoting.

What is .NET Remoting?
The entire set of services that enable .NET-centric applications to communicate with each other fall under the umbrella of .NET Remoting. Such applications can reside on the same computer, work on different computers in the same LAN, and even be scattered across the world in homogeneous networks and platforms. Of course, these homogeneous platforms and networks must host the CLR. Remoting is also a suitable tool for accessing objects running in different AppDomains or processes.
The .NET Remoting architecture provides a great deal of flexibility and enables you to use different transportation protocols, serialization formats, object lifetime schemes, and modes of object creation. In addition, programmers can plug directly into the flow of messages that each communication originates and can hook activities at various stages of the process.
As you've gathered by now, .NET Remoting is good at connecting applications under several different circumstances. However, looking at the technology in more depth, you realize that all forms of communication that are available through .NET Remoting are all special cases of just one particular form of communication. At the lowest level of abstraction .NET Remoting enables communication and data exchange between different AppDomains. So what's an AppDomain exactly?

Isolated Units of Processing
To understand AppDomains and their role in the overall remoting architecture, let's step back and think about the heart of .NET: the common language runtime. The CLR provides an execution environment for code that has been enriched with services like garbage collection, security, versioning, language-neutrality, threading, and many others. At this time, very few operating systems are capable of loading an executable built using .NET and running it within the context of a CLR instance. For this reason, .NET-based executables resort to an old trick. In Windows, when an executable doesn't match the system platform, a short piece of code, a stub, runs to send some text to the output console. In MS-DOS® this stub was used to send a message to inform users that the required runtime—Windows—was not running. Smart programmers usually changed that stub with another relatively standard piece of code that started Windows up with the recommendation of launching the application immediately. The same trick—a custom-made stub—is what allows you to execute any .NET-based executable in operating systems like Windows NT® and Windows 2000. Windows XP and newer systems, on the other hand, have a modified loader that looks directly into the source PE file for information that qualifies the file as managed code. The best resource on these runtime topics is Jeffrey Richter's book, Applied Microsoft .NET Framework Programming (Microsoft Press, 2002).
Today, a few system modules incorporate the ability to host the .NET CLR. The list includes Microsoft Internet Explorer 5.01 and higher, plus any user application that follows the instructions outlined by Steven Pratschner in his article "Microsoft .NET: Implement a Custom Common Language Runtime Host for Your Managed App" in the March 2001 issue of MSDN Magazine. Incidentally, the same CLR hosting mechanism will power one of the coolest features in the upcoming version of SQL Server™ (codenamed Yukon). In fact, a version of SQL Server that would host the common language runtime could support compiled stored procedures written in C# or Visual Basic® .NET.
After being initialized, in order to run the application's code, the instance of the CLR must obtain a pointer to an AppDomain. Normally, the CLR gets the first AppDomain defined within the process. This default AppDomain gets created during the CLR initialization. The host application can also create additional AppDomains and give them different settings for security, reference paths, and configuration files. At the end of the process shown in Figure 1, a managed executable is started up and begins processing its code within the boundaries of the (default) AppDomain.
Figure 1 AppDomain Creation 
In .NET, AppDomains are separate units of processing that the CLR recognizes in a running process. AppDomains are separated and isolated from one another in a way that resembles process separation in Win32®. Unlike Win32 processes, though, AppDomains provide for a more lightweight mechanism of isolation between processing units.
Before it can run, managed code not only needs an application domain but may also go through a process to verify that the code is type-safe and cannot cause memory faults. In Win32, this was one of the reasons to have a physical separation between process memory contexts. The requirement to run type-safe code allows the CLR to provide a level of isolation that's as strong as a process boundary, yet more cost-effective because of its lighter weight. Unlike Win32 processes, you can have several AppDomains running within the boundaries of the same .NET-based application. Individual domains can be stopped without stopping the entire process, but AppDomains are an atomic unit of processing, so you can only unload them as a whole.
Managed code running in an AppDomain is executed by a particular thread. However, threads and AppDomains are orthogonal entities in the sense that you can have several threads actively running during the execution of the AppDomain's code, but a single thread is in no way confined to running only within the context of a given AppDomain.
The CLR enforces isolation by preventing direct calls between objects living in different AppDomains. However, a tailor-made set of system services allows you to access an object that resides in an external AppDomain. This set of system services is exactly what .NET Remoting refers to. From an application's standpoint, an external AppDomain can transparently be another AppDomain in the same process, the default AppDomain in another process on the same machine, or even on a physically distant machine.

Interprocess Communication
To some extent, you can customize the way in which you set up any interprocess and interdomain communication using .NET Remoting services. In particular, you can decide whether to marshal objects by value or by reference. You can also control the object's activation and lifetime and choose the most suitable communication channel for transporting messages to and from remote applications. In addition, you can make modifications to the formatter that will encode and decode messages as they go over, or come in from, a channel.
You can invoke methods on a remote object via two basic techniques. But one way or another, the remote server objects must be reachable from the client. The first technique involves dynamically creating a full local copy of the remote server and accessing it on the local machine. The other possibility is to maintain an open channel with the remote machine and let parameters and return values travel back and forth to carry out the call. The first approach is referred to as marshal-by-value (MBV); the latter is known as marshal-by-reference (MBR).
Marshaling by value requires that the assembly defining that the server type be reachable by the client application. Clearly, this is not always the best approach, especially if you're working with large objects with dozens of methods. In fact, you risk consuming a significant portion of bandwidth and subjecting the client to a potentially lengthy wait only to execute one or two methods. With MBV, you cannot selectively decide what to download and cannot optimize the remote method call based on the needs of the application. MBV involves a kind of all-or-nothing approach that might not be suitable for all applications.
In addition, in order to be consumed by value, an object in .NET must be implemented as serializable, which is not always the case. For example, you cannot serialize an object representing or containing a database connection. This introduces an even more important reason to handle MBV scenarios with extreme care. Not all objects can be reasonably represented outside of their native environment. Not all of the information they hold makes sense once the object is transferred to the client. More often than not, in fact, the object has implicit dependencies and needs to access and use server-side resources that you cannot download to the client. For example, if one of the methods accesses a SQL Server table, how could you copy it to the client? Finally, MBV also raises some security concerns because of all the code information that is streamlined over a connection.
So when is MBV really a good option? When the object is not a large one, when you're going to make intensive use of it, when security and hacking are of no special concern, and, last but certainly not least, when the object has no dependencies on remote resources like files, databases, devices, or system resources. Otherwise, marshaling by reference is a more effective option.
When you marshal-by-reference, the client process receives a reference to the server object, rather than a copy. This means that any calls are resolved on the server within the native context of the object. The remoting infrastructure governs the call, collects all information about it, and sends it to the server process. On the server, the correct object is located and asked to execute the call using the client's arguments. When finished, results are packed and sent back to the client. MBR uses the network only for transmitting arguments and return values (see Figure 2).
Figure 2 Marshal by Reference 
The .NET Remoting implementation of MBR provides for a proxy/stub pair and a physical channel for network transportation. The proxy represents the remote object to the client as it simply mirrors the same set of methods and properties. Each client invocation of a remote method actually hits the local proxy which, in turn, takes care of routing the call down to the server. A method invocation originates a message that travels on top of a channel and a transmission protocol. Each message passes through a chain of sinks on each side of the transport channel. Sinks are nearly identical to Windows hooks; by defining and registering a sink, the programmer can perform a specific operation at a specific stage of the remoting process. Since the creation of the proxy takes place automatically, programmers have to do very little work other than creating an instance of the target object and issuing the call. If the target object resides in an external AppDomain, the remoting infrastructure creates a transparent proxy and performs the requested operation.
But how can the code determine whether a given object is local, lives in a remote AppDomain, or just doesn't exist? In spite of the sophisticated code that makes up the remoting infrastructure, programming remote objects is mostly a matter of setup. Once the client has been properly configured (more on this later), you normally create a new instance of the remote class using the New operator, no matter what type of classes you are creating. Clients must declare to the CLR which classes are remote and provide information to connect. Remote objects, in turn, must be publicly available and bound to a given channel. I'll come back to this point later on.

Channels and Formatters
A channel is the element in the .NET Remoting architecture that physically moves bytes from one endpoint to the other. A channel takes a stream of bytes, creates a package according to a particular protocol, and routes it to the final destination across remoting boundaries. As I mentioned earlier, a channel can connect two AppDomains in the same process as well as two machines over a WAN.
A channel object listens for incoming messages and sends outbound messages. In both cases, the messages it handles can be made of packets written for a variety of protocols. In programming terms, a channel is a .NET class that implements the IChannel interface. The channel object is also required to implement IChannelReceiver and IChannelSender if it is expected to act as a receiver and/or a sender.
The .NET Framework comes with two predefined channel classes, TcpChannel and HttpChannel, both of which work as senders and receivers. TcpChannel uses a binary formatter to serialize data to a binary stream and transport it to the target object using the TCP protocol. HttpChannel transports messages to and from remote objects using the SOAP protocol.
The server object publishes the list of supported channels and, based on this list, the client decides how to actually perform the call. Servers must register at least one channel. Channels are registered on a per-AppDomain basis and must have unique names in that context. However, on any physical machine only one channel can listen to a given port. In other words, even though channels are AppDomain-specific, you can't have more than one channel registered to work on a given port on a given machine at one time. Figure 3 details the steps that take a method request down to the server.
Figure 3 Method Request to Server 
Let's review what really happens when the execution of a remote method is required, noting that I still owe you several details about the settings that are necessary to enable the client to call into remote servers.
A client enabled to make remote calls on a range of objects creates an instance of the desired class using the language-specific operator for instantiation. As an alternative, it can use the system-provided Activator object. For example, consider the following fragment:
Dim o As RemoteObject
o = New RemoteObject()
o.TheMethod(p1, p2)
When this code compiles, any calls to RemoteObject are resolved through an external reference added to the project much as you would with Web references. The underlying code that actually handles method invocation on RemoteObject is an instance of RealProxy, the abstract class that provides base functionality for .NET Remoting proxies. The proxy works as if it were an object of the same class as the remote object. It collects and packs all information about the method call (name/value pairs with the method name and the argument list) into an object that implements IMessage. (IMessage has just one property: a dictionary called Properties.) The collection of information about the method call is then passed to the proxy's Invoke method. The proxy knows about the channel that the client application has chosen to carry the conversation out. So it delegates Invoke to open the channel and sends data to the remote server. Opening a channel, though, is just an abstract representation of what really happens.
First, the proxy creates an instance of the channel object and begins traversing its sink chain. Both inbound and outbound messages pass through a sequence of channel sinks that actually implement all of the core channel functionality. The chain normally contains at least two standard sinks that open and close the chain: the formatter sink and the transportation sink. In between the two, programmers can define as many custom sinks as needed.
The first sink in the chain is the formatter, which is responsible for serializing the message—that is, name/value pairs describing the method call—into a stream. The stream is then passed down through any custom sinks you may have registered and finally reaches the client transport sink. At this point, the content of the message is sent out towards the destination port.
So who's listening on the server on the specified port? Any .NET remote object must have a host application listening to all the ports supported by the object. Unlike clients, remote objects do not hold channels but declare which ones they plan to support. The object-specific host application receives the inbound message and makes it go up through the server-side sink chain. The first sink to get a handle to the message is the transport sink that actually receives the packets. Custom sinks then get their turn and, finally, the formatter sink rebuilds the original message. At this point, the host application for the server AppDomain has all the information it needs to execute the call. It looks for the assembly in the remote object's configuration file, instantiates the object, and invokes the method. The object identification and activation policy deserves more attention, and I'll discuss it shortly. Any return value follows the reverse path and moves from the server to the client in a way that is nearly identical to a client-to-server method call.
The formatter sinks are of two basic types: SoapFormatter and BinaryFormatter. The SoapFormatter serializes and deserializes an object in SOAP format, whereas the BinaryFormatter does the same but in binary format.

Object Activation and Lifetime
The host app designates the server classes that will be registered to service any incoming call and, along with it, what protocol, port, and name must be used to call the service. All this data is stored in the host application's configuration file. Other important pieces of information that you'll find there are the object activation and lifetime policies.
An instance of an MBR remote class can be activated by either the server or client. Server-activated objects are created by the server only when the client invokes the first method through the local proxy. By contrast, client-activated objects are created on the server as soon as the client calls New or the Activator object. In addition, server-activated objects can be declared as Singleton or SingleCall objects. A Singleton object has exactly one instance to serve all possible clients. A SingleCall object requires that each incoming call is served by a new instance.
When the client instantiates a server-activated object using new or any other equivalent technique, only the local proxy is actually created. The proxy is made ready for use, but nothing has happened on the server yet. The message for the server is built and forwarded only when the client invokes the first method on that instance of the object.
What happens next on the server AppDomain? If the object is registered as a singleton, then the host application attempts to locate the global running instance of the object. If such an instance exists, the request for execution is processed. Otherwise, the host creates the unique global instance of the remote server and forwards the request to it. What happens if two requests arrive at the same time? The remoting subsystem provides for them to be automatically serviced by distinct threads. This requires singleton objects to be thread-safe. This is not a mandatory programming rule, but more of a practical guideline for real-world scenarios.
If the object is expected to work as a SingleCall server, then the host simply creates a new instance of the object, executes the method, and routes any return values back to the client. Let's go over some pros and cons for all of these options.
Server-activated objects are more efficient and flexible because they give you a chance to control the activation process and implement a laxer policy for instantiation. On the other hand, if the server object has a nondefault constructor that takes arguments, you can only use it through client activation. The reason for this difference is that for server-activated objects the instantation of the proxy and the actual object occurs at different times so the host application defaults to the standard constructor.
Server-activated objects have two working modes. You can have one instance to service all calls from all clients or one instance for each call. In the latter case, each method call causes the host application to instantiate the object, execute the method, and return. After that, the object instance is left to the garbage collector. Though not completely impossible, preserving state from one call to the next is a bit impractical for SingleCall objects. In this case, the lifetime of the object instance is short and barely covers the duration of the method call.
For singleton objects, state management is possible but must be coded in the body of the object in much the same way as you can do with ASP and ASP.NET pages and even ASP.NET Web Services. The idea is that you use a shared cache that all clients can access, unless you invent some sort of filtering mechanism. Figure 4 lists a comprehensive summary of the features and working modes available for MBR remote objects.
If you use client-activated objects, the object's activity follows different guidelines that fall somewhere between the two previous options. Basically, each client-activated instance of a remote class has a 1:1 mapping with a particular client. The relationship is established upon creation—when the client calls New. The duration of the object's lifetime is subject to other rules that I'll discuss in a moment. Since each client holds its own personal instance of the remote class, persisting state is straightforward and does not require any special coding or other contrivance.
With the sole exception of server-activated SingleCall objects, the object's lifetime is managed by a new module called the lease manager. As mentioned earlier, a SingleCall object has a very short lifetime which is managed automatically by the CLR. As soon as the method returns, the object goes out of scope and becomes ready for the garbage collector. For singleton and client-activated objects, you need to count object references to determine when it's time to destroy them. In COM, this issue was solved by implementing reference counting. In .NET Remoting, the lease manager (working on a per-AppDomain basis) allows objects to be released even though clients still hold a reference to them. Let's quickly review the trade-offs of the two approaches to understand the rationale behind the change.
Reference counting would require clients, including distributed clients, to communicate with the server each time they connect or disconnect. The object maintains the number of currently active client instances, and when the count goes to zero the object destroys itself. On an unreliable network, chances are good that some object's reference count may never go to zero. If this weren't bad enough to preclude reference counting, remember that the continual sequence of connect/disconnect calls would generate significant network traffic.
The idea behind leasing is that each instance is leased to the client for a given amount of time fixed by the manager. The lease starts when the object is created. By default, each singleton or client-activated object is given a five minute lease. When the interval expires, the object is marked for deletion. During the lifetime, though, any processed client call resets the lease time to a fixed value (by default, two minutes), increasing or decreasing the overall lease time as needed. Note that the leasing is exclusively managed on the server and doesn't require additional network traffic, aside from that needed for normal method execution. The initial lease time and the renewal period can be set both programmatically and declaratively.
Another tool that .NET Remoting uses to control the object's lifetime is called sponsorship. Both client and server objects can register with the AppDomain's lease manager to act as sponsors of a particular object. When the lease of an object expires, prior to releasing the reference the remoting infrastructure gives sponsors a chance to renew the lease. By implementing sponsors, you can control the lifetime of objects based on logical criteria rather than raw ticks of the clock.
It's clear from this discussion of leasing that nothing can guarantee that clients will always find their server objects up and running. When a client attempts to access a server that is no longer available, a RemotingException exception is thrown. One way to resolve the exception is to create a new instance of the remote object and repeat the operation that failed. Like conflicts in ADO.NET batch update, remoting exceptions require applications to support tailor-made, context-specific coding.
It's interesting to note that all creation and working modes discussed so far don't strictly require you to code client-activated objects differently from, say, other objects destined to work as singletons. All options can be set declaratively and, at least in theory, each object can be configured to work in different ways by simply changing a few entries in the server's config file. However, although intriguing as a possibility, such duality is not realistic in practice because a real-world object might need to delve into the specific features of a given working mode. In other words, you carefully choose the configuration options for your remote object and then stick to those as long as the user's requirements are stable. For example, a singleton object might need to implement a state management engine for its own purposes. At a later time, if you set this object up to work as SingleCall or client-activated, such an engine becomes redundant, to say the least.

Writing Remotable Objects
How do you write a remotable object—a .NET class that can be instantiated from external AppDomains? The elegance and consistency of the .NET Framework, of which the remoting subsystem is a key part, makes this a trivial task. A remotable class is a .NET class that inherits from MarshalByRefObject, and thus the object will be marshaled by reference. At the highest level of abstraction, writing remotable classes is not really different from how you write XML Web Services. One minor difference is that you can write Web Services classes from scratch without inheriting any base functionality, but you must declare an infrastructure to expose public methods over the Web.
If any methods on the remotable class need to return a user-defined object, mark it as serializable using the <serializable> attribute. Likewise, as return values you should use only classes in the .NET Framework that are serializable or that extend from MarshalByRefObject. For example, the DataSet and DataTable objects are the only ADO.NET objects that can be remoted because they inherit from MarshalByValueComponent and support the ISerializable interface.
The following code illustrates the typical creation of a remotable MBR class in Visual Basic:
Namespace MsdnMag
    Public Class NorthwindSalesManager
        Inherits MarshalByRefObject
    •••
    End Class
End Namespace
Figure 5 contains the full source code of a sample remotable class called MsdnMag.NorthwindSalesManager. The project (available with this month's source code) produces an assembly called nwsales.dll. The class shows off one method, GetSalesReport, with two overloads: one returns all sales for a given year and one provides a sales report based on year and employee. As you can see, the object needs to query a back-end database (Northwind) to do its job. This inevitably leads to discounting the option of making the object available by value.
As an MBR entity, the class can be client-activated or server-activated. It doesn't need to have a nondefault constructor, so both client- and server-activated modes are fine. The object is expected to work as a one-off service and has no need to maintain state for each client. In light of this, you can discard the client-activated option and go for the server-driven activation.
So which mode setting is better now, Singleton or SingleCall? SingleCall—a short-lived instance that serves the request and dies—is certainly a fair choice. However, if you choose to use the object as a singleton, you can architect more efficient code and avoid querying SQL Server every time a request comes in. Singletons have a single instance running all the time until the lease expires. This fact, along with the behavior of the object, makes it suitable to cache results in memory as long as chances are good that multiple clients ask for reports regarding the same employee and the same year. The remoting architecture has much flexibility, but any form of optimization that you plan narrows the range of feasible options. So in real-world scenarios, switching from one working mode to another typically requires changes to the code.
Let's look at a server-activated object working with single calls. Once you have compiled the class and decided how it has to work, you must publish some information about the remote object for the clients to successfully connect. In particular, the server must define a host application to listen to incoming calls and process the request. In addition, the server object must list the channels and the ports to be used and define a resource name (URI) that clients will use to uniquely identify the remote class.
The host application can be a custom program (such as a console application) written by the same class author as well as IIS or dllhost.exe if you're accessing COM+ objects in Windows XP. If you use a custom host, then you must make sure it is up and running prior to issuing remote calls. With IIS, of course, this is not a realistic concern. Figure 6 contains the source code for a simple yet effective class for remotable objects. The source is named host.vb and when it's compiled it generates host.exe. The key statement is this:
RemotingConfiguration.Configure("Host.exe.config")
The host program reads the given config file and organizes itself to listen on the specified channels, formatters, and ports for calls destined for the remote object it governs. The config file contains information about the class name, the assembly that contains it, the required activation mode (client, singleton, singlecall), any URI, and leasing parameters. A typical config file for the remote object you saw in Figure 5 is shown in Figure 7.
The object can be activated through both HTTP and TCP channels. If it's TCP, the port must be 3412. The <service> tag describes how the server must be activated. If the object is server-activated, it's said to be a well-known object and described through the <wellknown> tag. If the object is client-activated then you must use the <activated> tag instead. The <wellknown> tag has a Mode attribute that you set to SingleCall or Singleton, as needed. The Type attribute is a comma-separated string in which the former token is the fully qualified name of the class and the latter is the name of the assembly containing the class. Finally, ObjectUri indicates the universal name of the object for server-activated objects. Client-activated objects don't need it. The object's URI is the endpoint of the URL with which the client identifies the target object location. Figure 8 shows the server configuration of the same object when client activated.

Remoting in Action
To round out the configuration of the object in Figure 5, let's walk through the steps necessary to use IIS, rather than a custom host, as the activation agent. First, create a new virtual directory (say, NWSales) and copy the object's assembly in its BIN subdirectory. In this case, the config file must be called web.config and resides in the virtual directory's root.
If you choose to use IIS as the activation agent, you must be aware of a few things. In the web.config file you cannot use the <debug> section. Only the HTTP channel is supported and any other channel you indicate is simply ignored. Since the host is a system process (inetinfo.exe), you cannot programmatically exercise any control on the activation process as you would do with a custom host. As a result, either you use a static config file or you arrange for a global.asax file to be in the virtual folder. In global.asax you can execute some custom code in the Application_Start event handler. Figure 9 shows how to proceed.