Ever since the early days of Windows programming, there has
been a gradual improvement in operating system stability. Much of this is due
to the separation of applications into distinct processes so that each has its
own area of memory. Developers have had to use many tricks to get these
applications to communicate with each other. Some of these methods included
using the system clipboard, sending windows messages, using the Visual Basic
(VB) SendKeys function or similar,
transferring “message” files, or declaring an area of shared memory. Each of
these methods had pros and cons and were generally “hacks” around the
separation of processes. When Microsoft's Component Object Model (COM) arrived,
the situation vastly improved, and such tricks were no longer needed, but COM
did introduce a number of issues with versioning, registration, and
administration that a generation of Windows developers has had to deal with.
Now with the .NET platform, you get cross-application communication built-in,
which provides you with an amazing amount of flexibility and control as to how
you want your applications to communicate with each other.

Every application on the .NET platform exists in its own unique
Application Domain. And every Application Domain is able to expose objects to
the outside world from any type of application—from simple console applications
to Windows Forms and Internet Information Server (IIS)–hosted applications. To
enable applications running in one Application Domain to communicate with other
applications in another Application Domain, you use remoting. Or you
could say remoting allows you to call methods and pass objects across
Application Domains.

The Remoting API on the .NET platform takes a different
approach than the other application programming interfaces (APIs), such as
Distributed COM (DCOM) and Remote Method Invocation (RMI) for communication and
message format. Rather than relying on a proprietary message and protocol, the
Remoting API uses well-established standards such as Simple Object Access
Protocol (SOAP) for messaging and Hypertext Transfer Protocol/Transmission
Control Protocol (HTTP/TCP) protocols for communication. This allows
applications to communicate just as easily across the Internet as they do
within the enterprise.

To understand how remoting works, imagine that you need to
create your own method of cross-application communication. Imagine that you
have an object that needs to accept calls from client applications across HTTP.
First, you’d need to define your object’s location as a URL of some kind. Then
you would need to choose a port that the object should listen to. You would
also need some way of publishing the interface of your object so that clients
would know what methods are available to call, and you would need a method of
describing the interface and handling the messaging between objects. The
creators of the .NET Framework have done just that and have exposed the
remoting functionality as a powerful way for programmers to start getting their
applications to communicate.

This chapter introduces the remoting framework and provides
many examples from real-world scenarios that occur during development. First,
we get an overview of how remoting works and look at the variety of choices
available to the developer as to how remoting is configured.

In the second part of the chapter, we produce a simple remoting
example that is gradually extended to use a range of remoting services. We also
take a look at how to deal with issues that developers face during the
development lifecycle in regard to deployment, debugging, administration,
documentation, and versioning while using the remoting framework.

Introducing Remoting

Remoting provides you with a number of choices as to
the method and configuration of communication used. Configuration areas are the
choice of channel, type of hosting application, the activation model, the
configuration method, and the method of exposing server metadata to the client
application.

The channel is the means of communication used by an
application to call to a remote object; the selection is between HTTP and TCP
(SMTP doesn't appear to be ready in Beta 2). The HTTP channel is mostly used
for Internet communication where firewalls need to be negotiated. The TCP
channel has a performance gain by using direct socket connections over an
arbitrary port selected by the developer. Both channels use SOAP for
communication; the TCP channel defaults to use a faster (but proprietary)
binary representation of the SOAP message, whereas the HTTP channel defaults to
use the XML standard. The TCP channel can also use the normal XML-formatted
SOAP messaging format.

The selection of the hosting application for the remote object
is the next choice. A hosting application must be configured to listen on a
channel and create the requested object in its own AppDomain when required. In Visual Basic 6, developers often used
IIS or COM+ services to host remote objects—the mysterious dllhost.exe that you
may see running in your Windows 2000 Task Manager is the hosting application
used by COM+. With the .NET Framework, you can still use these hosting
services, but you can gain more control by writing your own hosting
applications. When creating your own hosting application, as we do in the first
example, you may choose from a Console application, Windows Service, or Windows
Forms application.

Choice number three is the activation model for the remote
object. SingleCall objects are
stateless in that they handle only single calls from clients and do not hold
state between calls. After the call is handled, the object is discarded. Singleton objects can be shared between
multiple clients. They are often used when the resources needed to initialize
the object are large and the object’s state needs to be preserved between
method calls. You need to remember that Singleton
objects do have a default lifetime and may be recycled—we’ll see later how
developers can control the object’s lifetime to suit their needs. Client
Activated Objects (CAOs) allows a client application to create a remote instance
of the object for exclusive use and to preserve state between remote method
calls.

Choice number four is the method of configuring the remote
server. The host application can programmatically configure itself on startup
or a configuration file can be used. Of course, using an external file to hold
remoting configuration data enables changes to be made without a recompile of
the source code. The configuration information contains the channel, port,
activation model, type name, and assembly name of the object. A Uniform
Resource Identifier (URI), which clients use to identify the object, is also
specified.

The final choice is how the client obtains the remote object’s
metadata. Again comparing with Visual Basic 6, a server object’s interface
definition had to be on the client, either as a type library or an exported MTS
package, to enable the client VB code to make the call over DCOM. With
remoting, the situation is similar but improved by the .NET Framework’s use of
metadata. The first method is to set a reference to the remote object’s DLL in
the client project so that the compiler can extract the metadata. The second
method, but only if using the HTTP channel, is to use the soapsuds.exe utility
to generate a “proxy” class from the remote object’s URI. This proxy class can
then be included in the client project and used as if it is a local .NET type.
Internally, the proxy class will route the call to the remote object.

Remoting Architecture

An end-to-end picture of remoting is as follows. The host
application is loaded and registers a channel and port on which to listen for
incoming calls. The configuration file, if any, is read and an object’s
remoting information is loaded—the host application can now map a URI to the
physical assembly and instantiate the object when required. The client
application also registers the same channel and then attempts to create a new
instance of the remote class. The remoting system handles the request for a new
instance by providing a proxy object in place of the actual object on the
server. The actual object is either created immediately for CAOs or on the
first method call for Singleton/Singlecall objects—the remoting
framework takes care of this for you automatically. When the client calls a
method on the proxy object, the information is sent across the channel to the
remote object. The remoting system will then pass back the results of the
method across the channel in the same manner.

Creating a Simple Remoting Client Server

We’ll now create a simple client server application to
demonstrate the usage of the remoting framework. The code for the server side
is located in the ListServer directory of the CD—double-click on the solution
file ListServer.sln so that you load both the server and the hosting
application together. First, we’ll create the remote class named CompanyLists that contains the
functionality. All of the following code is on the CD.

Note

The code in this chapter uses localhost as the target
server—this will self-reference your local computer so that you may use both
the client and server code on the same PC. If you wish to place the server-side
code on a remote server, you will need to replace localhost with the correct
server name.

Creating the Remote Server Object

The remote server object contains all the server-side
functionality for our application:

1.Create a new Class
Library application in Visual Studio named ListServer.

2.Right-click
the default Class1.cs module in the Solution Explorer and choose Delete.

3.Right-click
the ListServer project in
the Solution Explorer, select Add | AddClass, and name your new class CompanyLists.cs.

4.Modify
the class declaration to inherit from MarshalByRefObject
so that a reference to the object can be passed remotely:

public class CompanyLists: MarshalByRefObject

{

}

5.Add
a private variable to the CompanyList class
that contains an array of strings:

private String[] Countries =
{"Spain","France","Italy"};

6.Add
a public method to CompanyList that
returns the array of strings defined in the preceding step. The complete class
should appear as:

public class CompanyLists: MarshalByRefObject

{

private String[]
Countries = {"Spain","France","Italy"};

public String[]
getCountryList()

{

return Countries;

}

}

The CompanyList class
can now be loaded by a hosting application for remoting. If you already have
classes that you’d like to make remoting aware of, it’s as simple as inheriting
from MarshalByRefObjectand then recompiling.

Note

If your class must receive and send objects during method
calls, you will need to use the <Serializable> custom attribute to pass
these objects by value or inherit from MarshalByRefObject
to pass by reference. An example of this is shown later. If your class
already inherits from another class, you’ll need to make the parent class
inherit from MarshalByRefObject because
multiple inheritance is not allowed in C#.

Creating the Hosting Application

Now we create the hosting application. This will be a console
application initially, but in the real world, this would probably be a Windows
Service application:

3.Add
a reference to the System.Runtime.Remoting
namespace and the ListServer project.

4.Add
the following usingstatements at the top of the code window to reference
the relevant namespaces:

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtim.Remoting.Channels.Http;

5.Add
the following code to the Main method.
This code creates an HttpChannel object
that uses port 8080. The RegisterChannel method
is then used to register the channel, after which the RegisterWellKnownServiceType method is called to register the class
with the remoting framework. The RegisterWellKnownServiceType
method contains three parameters that specify the type of the remoting
class, the URI, and the object activation mode. After this method has been
called, your class is then ready to accept requests from client applications.

static void Main(string[] args)

{

HttpChannel
myChannel = new HttpChannel (8080);

ChannelServices.RegisterChannel(myChannel);

RemotingConfiguration.RegisterWellKnownServiceType

(typeof(ListServer.CompanyLists),

"CompanyLists", WellKnownObjectMode.Singleton);

}

6.Build the console
application to create the ListHost.exe console application.

The CompanyList class
can now accept calls from remote clients. You’ll notice that we have chosen
port 8080 to listen to for client requests. The choice of port is rather
arbitary, although port 80 should be used to be firewall friendly. You need to
remember that a port can only be registered once per machine. To see what
happens when an attempt is made to register the same port twice, perform the
following experiment:

1.In Windows Explorer,
find and run the host application ListHost.exe.

2.While
the console application is running, run the same host application from within
the Visual Studio IDE. You may need to right-click the ListHost project in the Solution Explorer and select Set as StartUp Project to enable the
IDE to do this.

3.Figure 6.1 shows the
exception that occurs when the same port is reused.

Figure 6.1 The Exception Generated after an Attempt to Reuse
a Port

Creating the Client Application

The client application will be a standard Windows Application
with a main form, but it could also be any other type of .NET application. The
source for this project is located under the ListClient directory of the CD:

1.From the Visual
Studio menu choose File | New | Project. Select Windows Application,
and name the new project ListClient.

2.Rename
the Form1.cs file to ListClient.cs.

3.Add
a reference to the System.Runtime.Remoting
namespace and also to the ListServer.dll.

4.Add
the following using statements at the
top of the ListClient.cs code window to reference the relevant namespaces:

using ListServer;

using System.Runtime.Remoting;

using System.Runtime.Remoting Channels;

using System.Runtime.Remoting.Channels.Http;

5.Modify
the code in the Form1 constructor to
appear as follows so that a new HttpChannel
object is created and registered on application startup:

public Form1()

{

InitializeComponent();

HttpChannel c =
new HttpChannel();

ChannelServices.RegisterChannel(c);

}

6.Add
a button and a textbox to the form. In the button’s click event, add the following code. This code will create a
reference to the remote object by using the Activator.GetObjectmethod. Three
parameters are used by this method to specify the type of the remote class, its
URI, and the creation mode. The list of countries is then retrieved and used to
populate the form’s ListBox control:

8.Run the ListClient application. Click the button
to retrieve the list country list from your server object. In Figure 6.3, you
can see that the county list has been successfully obtained from the remote
object.

Figure 6.3 The Client Application

Understanding the Remoting Code

The host application simply needs to register a channel and
port using RegisterChanneland to register the remoting object using RegisterWellKnownServiceType. The RegisterWellKnownServiceTypemethod takes three parameters—the
type of the object, the object’s URI as defined by the developer, and the
creation mode. The first parameter provides the link between the hosting
application and the remoting object—this is why having a reference to your
class library’s DLL is necessary. Developers that have used previous versions
of Visual Basic may notice that we cannot magically determine the location of a
DLL using CreateObject. We must explicitly tell the compiler the DLL’s
location. This is actually a major benefit of the .NET Framework because we no
longer must trust that the Registry has accurate information to instantiate an
object.

Another important point is that an object does not “own” a
channel. You are free to register as many channels and objects in the hosting
application as you like. Communication on the server side is multithreaded, so
there is no need to worry about a request blocking a channel while processing
is done. You may also want to use one channel for Internet clients and another
for intranet clients and force this policy by screening ports on your proxy
server.

The client application must also register a channel, but in
this case the port does not need to be specified. This may seem strange at
first—doesn’t the client need to know which port to communicate with? The
confusion lies in the double life of the HttpChannelclass. Creating a HttpChannelobject actually creates a ClientChanneland a ServerChannel
object. The ClientChannelobject does not need
a port number because it can communicate with any port specified in the
URL. You could replace HttpChannelwith ClientChannelin the client code and everything would still work fine. The ServerChannelobject is given to us for free by the remoting framework so that the server
object can call back to the client if needed. By specifying a port when creating
a HttpChannel,we are allowing our client app to
“listen” on this port, but it has no influence on what port our app may talk
to. Also, if you are a lazy programmer, you can actually forget about
registering a channel altogether. The remoting framework will create one for
you the first time you attempt to reference a remote object. Try commenting out
the two lines of code that create and register a channel on the client (shown
in Step 5 in the previous section) and then rerun the application.

The client application also needs a reference to ListServer.dll
but for a different reason than the hosting application has a reference. The
hosting application needs the reference so that it can create the remoting
object to handle incoming requests. The client application needs the reference
only so that it can access the DLL’s metadata. As you will see soon, the
SoapSuds.exe utility removes the need to reference the DLL by extracting the
metadata and providing it to the client in the form of a proxy class.

To obtain a reference to the remote object, Activator.GetObjectis used. This method takes two parameters—the type of the object and the
remote object’s URI. The reference returned by GetObject is actually a reference to a proxy object that routes
messages to the remote server. The remote object is not created until the
client makes the first method call. This explains why the first time the button
is clicked in our example application that there is a delay—the remoting
framework is instantiating the remote object. And for those developers that
deleted the code to register the channel, there will be a slightly longer delay
while the framework sets up a default channel for you to use.

Note that if you are using the HTTP channel then the host
application can be tested by typing the remote object’s URI into a browser. Try
typing in http://localhost:8080/CompanyLists?wsdl into Internet
Explorer. As long as the host application is running and configured correctly,
you’ll see the SOAP definition of the remote class as it appears in Figure 6.4.

Figure 6.4 The SOAP Definition of the Remoting Class

Improving the Sample Application

Although the sample application is a good start and has shown
how to execute calls to a remote object, some areas need improving in order to
become a more real-world application. We introduce these improvements by adding
to the sample code one step at a time.

Adding Event Logging and Error Handling

A good coding standard would be to always have a hosting
application write to the event log information regarding startup success or
failure, the application name, server port number, and any other useful data.
We now add event logging and error handling to the sample hosting application.
This updated code is in the CompanyListHost2.cs file on the CD. The complete
code for the host is shown in Figure 6.5.

The code that writes messages to the event log is quite
straightforward. The WriteEntry method of the EventLogobject is used to write error messages from within the catch blocks. Error
handling has been added to trap exceptions caused while setting up the remoting
configuration.

Using the soapsuds Tool

The need for every client application to have a reference to the
remote assembly may be inconvenient for some third-party services. You use the
soapsuds.exe tool to create a proxy object from the remote assembly’s metadata
so that a reference to the assembly is not needed. We now modify the sample
application to use this proxy object by following the next few steps (The
updated ListClient code is located in
the ListClient2.cs file on the CD):

4.Remove
the project’s reference to ListServer from
the Solution Explorer window.

5.Right-click
the ListClient project in
the Solution Explorer window. Select Add | Existing Item and choose the ListServer.cs file to add
it to your project.

6.Modify
the button1_click method so that the
code is as follows:

private void button1_Click(object sender, System.EventArgs
e)

{

CompanyLists
cLst = new ListServer.CompanyLists();

listBox1.DataSource = cLst.getCountryList();

}

7.Build the
application.

Notice that the ListServer.cs file has taken the place of the
reference to the remote assembly. Inspection of the ListServer.cs code reveals
that this class is acting as a proxy by routing the remoting calls to the
remote object’s URI. This allows us to do away with the use of Activator.GetObject to obtain a remote
reference—we can now program against ListServer
as if it was a local class.

Note

The soapsuds utility has a range of command line options to
aid client-side development—see the Microsoft documentation for details. When
using this utility, it helps to remember that wsdl means Web Services Description Language and -gc means generate code. You’ll then be
able to amaze your friends and colleagues when you can type in soapsuds
commands from memory.

Using Configuration Files

Many settings to the configuration of .NET applications can be
achieved not only inside code but with configuration files as well. All of
these files use XML so that they are humanly readable and easily parsed by the
.NET Framework. With remoting, you can use configuration files to handle all of
the work necessary to expose and consume remoting objects.

You use the Configuremethod of the RemotingConfigurationclass to configure the remoting framework by specifying the configuration
file’s location. We now modify the ListHost
hosting application to read a configuration file at startup:

1.Open the ListHost project in Visual Studio.

2.Add
a new file to the project called ListHost.exe.config(which is also located on the CD)with the following
contents:

3.Modify the Main() method to use this configuration
file on startup (CompanyListHost3.cs on the CD):

static void Main(string[] args)

{

EventLog myLog =
new EventLog();

myLog.Source =
"ListHost";

bool failed =
false;

try

{

RemotingConfiguration.Configure(@"..\..\ListHost.exe.config");

myLog.WriteEntry("Configuration from ListHost.exe.cfg
successful");

}

catch (Exception
e)

{

myLog.WriteEntry("Failed
to configure host application: " +

e.Message,System.Diagnostics.EventLogEntryType.Error);

failed =
true;

}

if (failed)

{

System.Console.WriteLine("Errors at startup - see Event
Log.");

}

System.Console.WriteLine("Press
[Enter] to exit...");

System.Console.ReadLine();

}

Note that while running the host application in the Visual
Studio IDE, the bin\debug directory will contain the executable. You'll
therefore need to use the “..\..\” syntax in the file path to reach the
configuration file in your source code directory. A further improvement would
be to use a command line argument to specify the CFG file location. This would
help during deployment, and you could test out a variety of configuration
options easily without recompiling. Configuration files may also contain
multiple channels definitions and object URI entries.

The type parameter is of the format type = "TypeName,AssemblyName". These parameters can be
difficult to debug if they are wrong—no error message will be displayed during
the call to RemotingConfiguration.Configure. To help with debugging, the <debug loadTypes="true" /> attribute
has been added, which causes the types specified in the configuration file to
be loaded. Any errors in the spelling of a type name will then appear as a FileNotFoundException
type exception.

Note

The Microsoft standard for configuration files is that they
should have the same name as the assembly, but with a .config extension. For
example, myapp.exe will have the configuration file myapp.exe.config. This
configuration file must be placed in the same directory as the assembly to
enable utilities such as the .NET Framework Configuration tool to locate
configuration information.

On the client side a slightly different configuration file can
be used:

<configuration>

<system.runtime.remoting>

<application
name="ListClient">

<client>

<wellknown
type="ListServer.CompanyLists, ListServer"

url="http://localhost:8080/CompanyLists"/>

</client>

<channels>

<channel
type="System.Runtime.Remoting.Channels.Http.HttpChannel,

System.Runtime.Remoting"/>

</channels>

</application>

</system.runtime.remoting>

</configuration>

The client code also uses the Configuremethod of
the RemotingConfiguration class to read the configuration file on
startup. A client that uses a configuration file still needs a reference to the
remoting application’s DLL but can use the new keyword to instantiate the
class. The client-side configuration actually redirects the object creation to
the server and returns the remote reference. By using this method, it can be
difficult to know if you are successfully creating the remote object. A mistake
in the configuration file can cause the object to be instantiated locally
instead of remotely. To avoid such subtle bugs, you can simply close down the
remote hosting application and make sure that the object creation code causes
an exception when running the client.

Developing & Deploying…

Remoting Applications

Remoting applications on the .NET platform have a great deal of
flexibility as to how objects communicate with one another. It is even possible
to “plug-in” your own (or a third party’s) functionality to handle custom
formatting, encryption, and more. This makes it all the more important for
remoting issues to be considered up front in any design work. The areas that
need to be examined include the following:

nShould objects be sent over the
network by value or by reference?

nHow large are these objects?

nHow often will these objects need to
be sent?

nFor every remote method call, how
many bytes of data would a typical call contain?

nHow many client applications will a Singleton
object need to handle?

nWhat are the lifetime issues with
these objects? (that is, for how long must they maintain state?)

nCan a stateful object be used to
increase performance?

nWill your firewalls allow your
remoting calls through?

nDo your server-side objects need to
call back to the clients? If so, will these clients have their own firewalls?

nIf you need to shut down a hosting
application to upgrade the server object, how will the clients handle this?

Deployment of remoting applications seems quite easy—and indeed
it is. You could send the client-side executables with their configuration
files via e-mail to a friend and he would only need to copy them to a directory
and double-click the EXE to get started.

But wait, what happens if you want to move your server-side
objects to another server? When version 2 of the server-side functionality is
released, how do you let the client-side applications know? The solution to
these issues is largely dependent on the type of applications you create,
whether they are Internet- or intranet-based, and the number of clients that
must be administered. One idea to get you started is to have your client
configuration files actually located on your Web server. This would need to be
a server that is almost guaranteed not to have a domain name change. Instead of
having thousands of client configuration files distributed around the globe—you
now have only one. When client applications start up, they can get the
configuration file via HTTP from your server and always have the latest
version.

Most developers are happy to use Notepad to update
configuration files, but as the number of files increases, locating the
necessary files in the directory tree can be troublesome. The .NET Framework
provides you with a Microsoft Management Console (MMC) snap-in that serves as a
central location for .NET configuration. Although in Beta 2 this snap-in
appears to still need some improvement, it does hold promise of being a very
useful tool. To start the snap-in, open a command prompt window and change the
current directory to the installation directory of the .NET Framework, which
will be WINNT\Microsoft.Net\Framework\vx.y.z (where WINNT is
your windows directory and x.y.z is
the version of the .NET Framework). Type mscorcfg.msc to start the .NET
Framework Configuration tool. You will see a screen similar to Figure 6.6.

Figure 6.6 The .NET Framework Configuration Tool

To add ListHost.exe to the Applications node, simply click the Add an application to be configured hyperlink
and select the ListHost.exe file from the dialog. As long as your configuration
file is named ListHost.exe.config and located in the same directory as the
executable, you’ll be able to modify the remoting configuration settings. To
update the settings, right-click the Remoting Services node under
ListHost.exe and select Properties from the context menu.

Changing the Hosting Application to a Service

Hosting all of your remoting objects from console applications
does appear strange at first sight. It’s the 21st century and we still haven’t
completely got rid of those character-based applications! The fact is that
console applications do provide a good environment for debugging applications
that use remoting—you can immediately see if your hosting application is
running, and you can easily send debug messages to the console window in
real-time while you run your client-side app.

Once your server-side classes are ready for deployment, a
Windows Service provides a better hosting environment. System administrators
can easily start and stop your service, you can view your service from within
Visual Studio’s new Server Explorer, and you can guarantee that your service
will be started after a reboot of the server. The service application we will
create is located under the ListService directory on the CD. To create a new
hosting service, follow these steps:

1.Load the ListHost project into Visual Studio.

2.Select
and copy all the code from within the Main()
method.

3.Select
File | New | Project. Select the Windows
Service template and type in ListService
for the project name. Make sure that the Add to Solution option is set and then click OK.

4.While
the Service1.cs file is in design view, use the Properties window to set the
service name to ListService.

5.Switch
to code view and paste the code you copied in Step 2 into the OnStart() method. Remove any code that
was used to write to the console window. Replace any text within the code that
refers to ListHost to be ListService.

6.Add
the line using System.Runtime.Remotingto the start of
Service1.cs.

7.Switch
back to the Service1.cs design view. At the base of the Properties window,
select the Add Installer link—see
Figure 6.7.

Figure 6.7 Setting the Properties of a Windows Service
Application

8.Select
the serviceProcessInstaller1 component (if this component is not
visible, double-click the ProjectInstaller.cs
file in the solution explorer) and set its Account property to Local System.

9.Copy
the ListHost.exe.config file to the winnt\system32 directory and rename as ListService.exe.config.

10.Change
the method call that reads the configuration file to the following:

RemotingConfiguration.Configure("ListService.exe.config")

11.Build
the ListService project.

12.Open
a command prompt window and change the current directory to the installation
directory of the .NET Framework, which will be WINNT\Microsoft.Net\Framework\vx.y.z
(where WINNT is your windows
directory and x.y.z is the version of
the .NET Framework).

13.Type
installutil appPathwhere appPath
is the directory path to ListService.exe. This will install your service.

14.The service is now
installed. You can now start the service by using the Server Explorer from
within Visual Studio.

You can also view the Event Log from the Server Explorer making
Visual Studio the central hub of your development activities. Notice that the
configuration file was placed in the winnt/system32 directory because this is a
Windows Service application. If you need to keep the configuration file
together with the executable, you will have to use the absolute path.
Installing the service with the installutiltool has to be done
only once. To update the executable, simply stop the service and rebuild the
project.

Using the TCP Channel with the Binary Formatter

Within a corporate intranet, you can gain more speed by using
the TCP channel. To change the sample application to use the TCP channel all
you need to do is do a search and replace of every “Http” with “Tcp” within the
configuration files. The TCP channel uses binary formatting by default, whereas
the HTTP channel defaults to SOAP formatting. Two downsides of using the TCP
channel is that communication may be blocked by firewalls, and you cannot use
your browser to examine the SOAP description of your hosting application.

Summary of the Improved Sample Application

Your sample application now contains enough bells and whistles
to provide a base for a real-world multitier application. You have seen how to
host your remoting objects from within a Windows Service, how to write to the
event log, how to handle exceptions on startup, and how clients can easily
communicate with your remote objects. To further enhance the application you
could connect to a database to obtain various lists of data that are in common
use across all corporate applications—countries, clients, customers, languages,
application settings, and so on. On the client side, you could then subclass a
ComboBox control and add a property called ListType,
which would load the corresponding list of items from your remote object on
initialization. This control would save development time and provide a
standardized user interface. ASP.NET applications could also use your remote
objects in the same way.

Creating an Intranet Application

The remoting framework provides fine control over how objects
are sent to and from remote applications and also how objects are created and
destroyed. We now look at an example of how you can use these features in a
remoting application.

Object Lifetime and Leasing

In the COM world, object lifetime was controlled by reference
counting. As clients disconnected from the server object, the reference count
was decremented until it reached zero. The server object was then unloaded
immediately, and any hold on system resources was released. With the .NET Framework,
no reference counting occurs. Instead, an object is marked to be garbage
collected when no other object holds a reference to it. Because the garbage
collector cannot detect remote references (because they are in another
AppDomain), .NET uses another method for handling object lifetime called leasing.

Objects have a default lease time—when this time has passed, the object will be ready for garbage
collection provided there are no references to the object from its own
AppDomain. An object can change its own lease period on startup or even set it
to infinity to maintain state forever (forever = until a server reboot!).
Clients are able to renew this lease if they wish to keep communicating with
the same object instance. Also, the client can register a sponsor for a lease.
When the lease expires, the sponsor is given the opportunity to renew the
lease.

We now create a sample
application that uses the leasing features of the remoting framework. The
source code for this project is in the CountServer directory—opening up the
solution file CountServer.sln will make sure that both the server and the
hosting application are loaded into Visual Studio.

Creating the CountServer Project

This project contains the server-side functionality. The Count class implements a counter that
can be incremented and decremented with the incand decmethods
respectively:

1.Create a new Class
Library application in Visual Studio named CountServer.

2.Right-click
the default Class1.cs module in the Solution
Explorer and choose Delete.

3.Right-click
the ListServer project in the
Solution Explorer, select Add | Add Class and name your new class Count.cs.

4.Add
the following code to Count.cs:

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Lifetime;

namespace CountServer

{

public class
Count: MarshalByRefObject

{

private int
mVal;

public
Count()

{

mVal =
0;

}

public
override Object InitializeLifetimeService()

{

ILease
lease = (ILease)base.InitializeLifetimeService();

if
(lease.CurrentState == LeaseState.Initial)

{

lease.InitialLeaseTime = TimeSpan.FromSeconds(5);

lease.RenewOnCallTime
= TimeSpan.FromSeconds(1);

lease.SponsorshipTimeout = TimeSpan.FromSeconds(5);

}

return
lease;

}

public int
inc()

{

mVal++;

return
mVal;

}

public int
dec()

{

mVal--;

return
mVal;

}

}

}

This code is quite straightforward except for the InitializeLifetimeServicemethod. Every remoting object has this method because InitializeLifetimeServiceis a method of the inherited MarshalByRefObject
class. This method obtains the current lease for the object, and by
overriding this method, an object can control/set its own lease properties.
These lease properties can be set only before the object has been marshaled to
the client—the CurrentState property
is used to check that the lease is in its initial state and can therefore be
modified. The three lease properties used in the code are the following:

nInitialLeaseTimeThe time of a lease.
The object will be ready for garbage collection after this amount of time.
Setting this property to null gives an infinite lease time.

nRenewOnCallTimeEvery call to the
object will increase the lease time by this amount.

nSponsorshipTimeoutWhen
the lease has expired, the lease will contact any registered sponsors. The
sponsor then has the opportunity of extending the lease. The SponsorshipTimeout value is the amount
of time that the object will wait for a response from the sponsor. The sponsor
class will be introduced shortly in the client-side code.

These default lease settings can also be placed within the
configuration file as follows:

Theunits of time used in the configuration file are D for days, M for minutes, S for
seconds, and MS for milliseconds.

Note

For a lease on the server to contact a sponsor on the client,
the client must register a ServerChannel to listen on a port.
If the lease attempts to contact your client-side sponsor and you do not have a
ServerChannel,
the contact will fail and the remoting object will be deactivated after the
specified SponsorshipTimeoutvalue. You will not receive an error in
this situation.

Creating the CountHost Project

This host application will configure the Count class for remoting as a Singleton
object. Being a Singleton object,
it is shared between all client applications:

1.Add a new Console
Application project named CountHost to
the current solution and add a reference to the CountServer project.

2.Add
the call to RemotingConfiguration in
the main method and reference the System.Runtime.Remoting
namespace so that the complete console application code appears as follows:

using System;

using System.Runtime.Remoting;

namespace CountHost

{

class Class1

{

static void
Main(string[] args)

{

try

{

RemotingConfiguration.Configure(@"..\..\CountHost.exe.config");

}

catch
(Exception e)

{

System.Console.WriteLine("Failed to configure hostapplication:

" +e.Message,System.Diagnostics.EventLogEntryType.Error);

}

System.Console.WriteLine("Press [Enter] to exit...");

System.Console.ReadLine();

}

}

}

3.Create
the configuration file named CountHost.exe.config and place in the
project directory:

<configuration>

<system.runtime.remoting>

<application
name="CountServer">

<channels>

<channel
displayName="MyChannel"

type="System.Runtime.Remoting.Channels.Http.HttpChannel,

System.Runtime.Remoting" port="8085" />

</channels>

<service>

<wellknown
displayName="MyService" mode="Singleton"

type="CountServer.Count,CountServer"

objectUri="CountServer" />

</service>

</application>

<debug
loadTypes="true" />

</system.runtime.remoting>

</configuration>

4.Build
the project to produce the hosting application—CountHost.exe.

Creating the CountClient Project

The CountClient project
is a Windows Application that will remote to the server-side Count object and
update the counter value. The app will also have two buttons that allow us to renew
the lease and to also add a sponsor for the object. Follow the next steps to
create the project or alternatively access the code from the CountClient
directory on the CD.

1.Create a new Windows
Application for the client side called CountClient.

2.Add
four buttons to the form—btnInc, btnDec, btnRenew, and btnSponsor with
the captions—“Inc”, “Dec”, “Renew Lease”, and “Add Sponsor”. Also add a textbox
called txtValue.

3.Add
click event handlers to each button and add the following code to the form:

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Lifetime;

namespace CountClient

{

public class
Form1 : System.Windows.Forms.Form

{

private
System.Windows.Forms.Button btnInc;

private
System.Windows.Forms.Button btnDec;

private
System.Windows.Forms.Button btnRenew;

private
System.Windows.Forms.Button btnSponsor;

private
System.Windows.Forms.TextBox txtValue;

private
System.ComponentModel.IContainer components;

private
CountServer.Count objCount;

private
ClientSponsor mSponsor;

private
ILease mLease;

public
Form1()

{

InitializeComponent();

RemotingConfiguration.Configure(@"..\..\CountClient.exe.config");

objCount
= new CountServer.Count();

}

private void
btnInc_Click(object sender, System.EventArgs e)

{

txtValue.Text =
objCount.inc().ToString();

}

private void
btnDec_Click(object sender, System.EventArgs e)

{

txtValue.Text = objCount.dec().ToString();

}

private void
btnRenew_Click(object sender, System.EventArgs e)

{

mLease =
(ILease)RemotingServices.GetLifetimeService(objCount);

try

{

mLease.Renew(System.TimeSpan.FromSeconds(10));

MessageBox.Show(this,"Lease renewed for 10 seconds");

}

catch

{

MessageBox.Show(this,"Lease has expired");

}

}

private void
btnSponsor_Click(object sender, System.EventArgs e)

{

mLease = (ILease)RemotingServices.

GetLifetimeService(objCount);

mSponsor
= new ClientSponsor();

mSponsor.RenewalTime = TimeSpan.FromSeconds(15);

try

{

mLease.Register(mSponsor);

}

catch

{

MessageBox.Show(this,"Lease has expired");

}

MessageBox.Show("Sponsor registered with object");

}

}

}

4.Create
the client-side configuration file:

<configuration>

<system.runtime.remoting>

<application
name="CountClient">

<client>

<wellknown
type="CountServer.Count, CountServer"

url="http://localhost:8085/CountServer"/>

</client>

<channels>

<channel
type="System.Runtime.Remoting.Channels.Tcp.TcpChannel,

System.Runtime.Remoting"
port="8011"/>

</channels>

</application>

</system.runtime.remoting>

</configuration>

Understanding the Leasing and Sponsorship Code

The increment and decrement buttons simply call the
corresponding methods on the server-side Count
object and display the result in the textbox. By observing the returned
value, you can determine if you are still using the original class instance.

The Renew Lease button renews the lease of the current
server-side Count object. To do this,
the lease is obtained by calling GetLifetimeServiceon the remote
object. A remote reference to the server-side lease is then returned and the Renew
method is called on the lease. Note that the lease is also acting as
a remote object in this scenario. The Renew method takes a TimeSpan parameter that specifies the
new lease time.

The Add Sponsor button registers a sponsor so that you can
receive a notification when the lease has expired. The code obtains a remote
reference to the lease, creates a sponsor, and then registers the sponsor with
the lease. In the preceding code, the sponsor will set the lease time to 15
seconds when notified by the server-side lease. By running the client-side
application, you will see a form as shown in Figure 6.8.

Figure 6.8 Controlling Object Lifetime with Leases and
Sponsors

To test the object lifetime features of .NET remoting, click on
the Inc button two times so that the value in the textbox is 2. The InitialLeaseTime
set by the server-side Count object
is 5 seconds—if you wait more than 5 seconds and then click on Inc
again, you will notice that the counter has been reset. The remoting framework
has destroyed the original instance after 5 seconds and has created a new
object to handle the latest call.

The server-side lease property, RenewOnCallTime,has
a value of 1 second. This will cause 1 second to be added to the lease time on
every call to the remote object. You can test this by clicking on the Inc
button quickly 20 times—you will notice that after waiting 20 seconds, and clicking
Inc again, that the counter has not been reset.

Clicking Renew Lease will
set the current lease time to 10 seconds. Again, by clicking on Inc a couple of times, waiting about 8
seconds, and then clicking Renew Lease,
you will notice that the counter’s life is extended. Clicking Add Sponsorhas the effect of having a permanent Singleton object on the server. The sponsor will always set the
lease time of the remote object to 15 seconds whenever it is notified by the
server that the lease has expired. After the client application is closed, the
server site lease will eventually attempt to notify the client of lease
expiration. In our example, 15 seconds will pass while the server lease waits
for an answer. When that answer doesn’t arrive, the remote object will be shut
down.

As a final experiment, start up two instances of the
CountClient.exe and run them side-by-side. By clicking on the Inc and Dec buttons of each application, you will see that they both share
the same Singleton instance of the
server-side object. Also, if all client-side applications are shut down, the
server-side component will still maintain its state if a new application calls
the component before its lease expires.

Debugging …

Remoting Applications

Anyone that has started learning the .NET remoting framework
will know how easy it is to accidentally stop an application from working. A
wrong port number or a misspelled type name can take some time to track down.
In a perfect world, you would get an error message like “You tried to communicate
with server xyz on port 8050. There is nothing listening on that port but 8051
has something that may interest you.” To help avoid these problems, a base
application might help—this application would contain the three projects needed
for a remoting app (server, host, and client) plus any boilerplate code and
configuration files. This application could then serve as a starting point for
all your remoting applications.

Another method to help (or avoid) debugging your applications is
to start with the simplest case. A simple “test” method on each remote object
could be used to test the communication channels between the tiers of your
application. Such an approach is better than calling a complex method on an
object that may contain other object references and serialized objects as
parameters—there would just be too many places where something could go wrong.
This “test” method would also help in the construction of a monitoring
application that could ping your remote objects every few minutes and e-mail an
administrator if an object does not respond.

Client Activated Objects

The server-activated objects that we have been using so far in
this chapter have been of two types, Singleton
and SingleCall. The third type of
remoting object is the CAO or Client Activated Object, which allows a client
application to create a remote instance of the object for exclusive use,
similar to the way that clients do in the COM world. These objects can maintain
state without you having to worry about another client connecting to the
remoting object and changing its state without your knowledge.

Making your objects ready for client activation is as easy as
modifying the configuration file on the server. For example, the CountClient’sconfiguration file
has the following section:

<service>

<wellknown
mode="Singleton" type="CountServer.Count,CountServer"

objectUri="CountServer" />

</service>

To change this to a CAO, the activatedattribute
is used with only the typeparameter instead of the wellknownattribute:

<service>

<activated
type="CountServer.Count,CountServer">

</activated>

</service>

The client-side configuration file then requires a modified
<client> specification that uses the same activatedattribute
parameters as the server-side configuration file:

<client url="http://localhost:8085">

<activated
type="CountServer.Count,CountServer"/>

</client>

When the client-side uses newto create a remote instance,
the remote object is created immediately for the exclusive use of the client.
Lifetime leasing and sponsorship need to be used in the same way as in the
previous example—even though the object “belongs” to the client, it still has a
lease that may expire, causing the object to lose state.

Sending and Receiving Objects by Value

For more complex remoting applications, you may need to pass
objects as parameters to remote method calls or receive such objects in return.
For example, instead of passing a customer name and a customer ID in separate
calls, it is more efficient to create a Customer object containing the required
information and send the whole object to the server in one call. To achieve
this, the remoting framework needs to be able to serialize your class so that
it can be sent over a channel.

The [serializable]attribute is used
to specify a class as being serializable and able to be remoted by value. Using
the customer example, the class definition would appear as follows:

[Serializable]

class Customer

{

public Customer()

{}

int ID;

String Name;

}

Note

It is important to consider the suitability of a class for
remoting. If the class can hold large amounts of data and must be sent over a
slow connection, application performance will suffer. Also, some types of data
(for example, a file path) would have no meaning on a remote server.

Sending and Receiving Objects by Reference

For overly large objects, passing them by reference to remote
servers may be more efficient. This is roughly equivalent to simplified
remoting—the remoting framework will create a proxy for your object on the
server. As the server calls methods on your object, the proxy will route the
calls to the real object on the client side. As you are controlling the
creation of the object instance and handling the calls explicitly, you don’t
need to consider ports, channels, and object lifetime issues (although if you
would like the server to call-back to your client object, keeping a reference
to it would be a good idea to prevent it from being garbage collected).

For a class to be sent by reference, it is necessary for the
class to inherit from MarshalByRefObject. The customer
class would then appear as follows:

class Customer: MarshalByRefObject

{

public Customer()

{}

int ID;

String Name;

}

Creating Service-Based Applications

A major improvement of .NET components compared to legacy COM
components is the ability to use side-by-side deployment. Upgrading COM
components is an all-or-nothing affair, which can cause problems with client
applications relying on a specific version of a component. With the .NET
Framework, you can have different versions of the same component running at the
same time. To achieve this with your remoting applications, you need to give
your server-side assemblies what is known as a strong name.

Building a Versioned Remoting Application

A strong name is a
unique identifier for an assembly, which is generated by combining a text name,
the version number, culture information (if it exists), a public key, and a
digital signature. This may sound complicated, but it is in fact quite easy. We
now create a remoting class and build the assembly with a strong name. The
following code is in the VersionServer directory on the CD:

1.Create a new Class
Library application in Visual Studio named VersionServer.

2.Right-click
the default Class1.cs module in the
Solution Explorer and choose Delete.

3.Right-click
the ListServer project in the
Solution Explorer, select Add | Add
Class and name your new class Test.cs.

4.Add
the following code to Test.cs. The getVersion
method will be used to return the current version string back to the client
application:

using System;

using System.Windows.Forms;

using System.Reflection;

namespace VersionServer

{

public class
Test:MarshalByRefObject

{

public
Test()

{

}

public
String getVersion()

{

return
Assembly.GetAssembly(this.GetType()).

GetName().Version.ToString();

}

}

}

5.Now,
use the strong name utility (sn.exe) to generate a new strong name key. To do this,
use the -k parameter with the output
file name. From the Visual Studio .NET Command Prompt type in sn –k mykey.snk. Copy the new key file
to the source code area of the VersionServer
project.

6.Now,
add the key to the assembly manifest. Open the AssemblyInfo.cs file, which contains the assembly attributes, and
find the AssemblyKeyFileattribute. Add the path to the key file to the
AssemblyKeyFileattribute as shown here:

[assembly: AssemblyKeyFile("..\\..\\mykey.snk")]

7.Also,
set the desired version number using the AssemblyVersionattribute as shown
here:

[assembly: AssemblyVersion("1.0.0.99")]

8.After
building the VersionServer.dll, you need to install the assembly into the
Global Assembly Cache (GAC). The GAC is located in the Assembly directory under
the Windows system directory. For example C:\WINNT\Assembly. To install the
assembly, you can drag and drop the DLL into the GAC or you can use the
gacutil.exe utility.

9.Now update the
version to 2.0.0.0. Rebuild the
project and repeat Step 8. Update the version to 3.0.0.0 and repeat Step 8 again. You will now have three versions
of the VersionServer in the GAC, as
shown in Figure 6.9.

Figure 6.9 Installing Multiple Versions in the GAC

Creating the VersionHost Project

The VersionHost project
is a simple console application that will host the versioned components. The
code for the project is located in the VersionHost directory on the CD. This
code is the same as that used for the earlier examples except a slightly
different configuration file is used (see Figure 6.10). In this configuration
file, the required version has been added to the wellknownattribute.
Even though you earlier created a version 3 of the assembly, you are able to
choose version 2 (or any other version) by modifying this configuration file.

Figure 6.10 Configuring a Versioned Remoting Application

<configuration>

<system.runtime.remoting>

<application
name="CountServer">

<channels>

<channel
type="System.Runtime.Remoting.

Channels.Http.HttpChannel,System.Runtime.Remoting"

port="8085"/>

</channels>

<service>

<wellknown
mode="SingleCall"

type="VersionServer.Test,VersionServer,Version=2.0.0.0"

objectUri="VersionServer2" />

</service>

</application>

<debug
loadTypes="true" />

</system.runtime.remoting>

</configuration>

This version setting will be used whenever a server activated VersionServer object is needed. This
means that clients requesting a server activated object from a URI are not able
to request a specific version—versioning is determined by the server. To enable
client requests for different versions, you need to use a different URI for
each version. You can do this by adding extra wellknownattributes
to the configuration file, as shown in Figure 6.11.

Figure 6.11 Including Multiple Versions in a Configuration
File

<wellknown mode="SingleCall"

type="VersionServer.Test,VersionServer,Version=2.0.0.0"

objectUri="VersionServer2" />

<wellknown mode="SingleCall"

type="VersionServer.Test,VersionServer,Version=3.0.0.0"

objectUri="VersionServer3" />

Note

If the version is not specified in the server-side
configuration file, the latest version available will always be loaded.

Creating the VersionClient
Project

The VersionClient project
will be used to connect to a specific version of VersionServer. This will be done by specifying the corresponding
URI in the client-side configuration file. Follow the next steps to create the project
(or access the code from the VersionClient directory on the CD):

1.Create a new Windows
Application called VersionClient.

2.Add
a button to the form called btnGetVersion.

3.Add
a click event handler to the button and add the following code to the form. The
button will retrieve the version information from the remote object and display
it within a message box:

using System;

using System.Drawing;

using System.ComponentModel;

using System.Windows.Forms;

using System.Runtime.Remoting;

namespace VersionClient

{

public class
Form1 : System.Windows.Forms.Form

{

private
System.Windows.Forms.Button btnGetVersion;

private
System.ComponentModel.Container components = null;

private
VersionServer.Test objRemote;

public
Form1()

{

InitializeComponent();

RemotingConfiguration.Configure(

@"..\..\VersionClient.exe.config");

objRemote = new VersionServer.Test();

}

private void
btnGetVersion_Click(object sender,

System.EventArgs e)

{

MessageBox.Show(objRemote.getVersion());

}

}

}

4.Create
the client-side configuration file. The VersionServer2
URI is used to connect to version 2 of the remote component:

<configuration>

<system.runtime.remoting>

<application
name="VersionClient">

<client>

<wellknown
type="VersionServer.Test, VersionServer"

url="http://localhost:8085/VersionServer2"/>

</client>

</application>

</system.runtime.remoting>

</configuration>

5.Start the
VersionHost.exe console application and then build and run the VersionClient project. Clicking the
button will display the version of the remote object—see Figure 6.12.

Figure 6.12 Including Multiple Versions in a Configuration
File

Testing Side-By-Side Execution of Remote Objects

As a final experiment, we get two versions of the remote
object running side by side. To do this, keep the VersionClient application running. Then open up the client-side
configuration file and change the URI from VersionServer2
to VersionServer3—this will not
impact the running application because the configuration file is read only on
startup. Now find the VersionClient.exe executable in Windows Explorer and run it.
After clicking the button, you’ll see that version 3 of the remote object is
now being used. Click the button of the first application instance and version
2 is still available! Both application instances can run independently on the
client, while multiple versions of the server-side objects can handle client
requests at the same time.

Summary

Remoting is used to allow .NET applications to
communicate with each other across TCP or HTTP protocols. This communication
takes place across a channel which uses SOAP to format message calls. These
SOAP messages can either be XML formatted or sent as a binary stream. Although
the HTTP channel is suitable for applications distributed on the Internet, the
TCP channel is faster and is often used on corporate networks.

Server-side objects must be hosted in a hosting application to
expose them to requests from client applications. A hosting application may be
a Console, Windows Service, or Windows Forms application. When the hosting
application starts, it must register a channel to listen for client requests by
calling ChannelServices.RegisterChannel. The host will then register
remoting configuration information with the remoting framework either in code
(using the RemotingConfiguration.RegisterWellKnownServiceTypemethod) or
by using a configuration file (using the RemotingConfiguration.Configuremethod).

Remoting objects have three activation models—SingleCall, Singleton,and Client Activated Objects (CAO). SingleCall objects are stateless,
whereas Singleton objects are
stateful and able to be shared between client applications. CAO objects are
created by a client application for exclusive use and they preserve state
between remote method calls.

For a client application to be compiled in Visual Studio.NET,
the remote server classes metadata is needed. The easiest method is to
reference to the remote object’s DLL in the client project. The other method is
to use the soapsuds.exe utility to generate a proxy class from the remote
object’s URI.

For a client application to use remoting, a channel must be
registered and the remoting framework configured in a similar manner to that
used in the hosting application. If a configuration file is not used on the
client and a proxy class is not available (from the soapsuds utility), the Activator.GetObject method must be used
to create a reference to the remote object.

The lifetime of a remoting object is controlled by a lease.
Objects have a default lease time after
which they can be garbage collected. An object may change its default lease
time on startup by overriding the InitializeLifetimeServicemethod. Clients may also renew a lease to
keep a remote object active. When a lease expires, the remoting framework will
notify any registered sponsors so that a sponsor may renew the lease if
required.

An assembly in which
remoting classes reside may be versioned by using a strong name. A strong name
allows the assembly to be placed in the Global Assembly Cache (GAC) so that it
may be located by the remoting framework. The server-side configuration file is
used to expose a specific version of a component for remoting clients to
access. It is possible for multiple versions of remoting objects to run
side-by-side.

Solution Fast Track

Introducing Remoting

xRemoting allows cross-application
communication, whether they are located on the same PC or across the Internet.

xChannels are used as the
communications mechanism—HTTP and TCP channels may be used.

xServer-side objects need a hosting
application to handle incoming requests. A hosting application may be in the
form of a console application, Windows Service, forms-based app, IIS, or COM+
service.

Creating a Simple Remoting Client Server

xAll remoting objects must inherit
from MarshalByRefObject.

xHosting applications use the RegisterWellKnownServiceType method of
the RemotingConfiguration class to
register objects for remoting.

xSingletonsobjects only have a single instance and handle multiple client requests.

xSingleCall
objects do not maintain state. They handle a single request and are then
recycled by the remoting framework.

xRemoting applications that act as
servers must listen on a port as specified by the developer.

xExternal XML configuration files may
also be used to configure remoting on both the server and the client.

xHosting remote objects in a Windows
Service application eases the administration of server-side remoting objects.

xThe default values of the lease may
be specified by the remote object on startup.

xA client application may control the
lease to keep a remote object active.

xA sponsor can be attached to a lease.
When the lease has expired, the sponsor will be notified so that the lease may
be extended if required.

Creating Service-Based Applications

xVersioned assemblies require a strong
name so that they can be uniquely identified by the .NET Framework.

xTo generate a strong name, a strong
name key is needed. The sn.exe utility is used to create key files.

xVersioned assemblies should be placed
in the Global Assembly Cache (GAC)—the .NET Framework will search the GAC for
strong-named (shared) assemblies.

xFor server activated objects, the
server configuration file is used to map a URI to the version of an assembly.

Frequently Asked Questions

Q:
If I have a Singleton object to
handle multiple clients and it only listens on a single port, doesn’t this
create a performance bottleneck?

A:
Don’t worry. Remoting objects are multithreaded so that one request does
not block another.

Q:
With .NET, it seems much easier to maintain state on the server. Will this
change the way applications are developed?

A:
The stateless model of development is often the most scalable and robust
architecture. The lessons learned with Windows DNA multitier development still
apply today.

Q:
It is also easier now to have the server perform callbacks to the client
side, in what situations can this be used?

A:
Callbacks are easier with .NET as compared to VB in the past. They are also
very interesting to program, but in a business setting, you should use them
only when you have no other choice. For example, a callback to notify a user of
a certain situation may be better handled with a generated e-mail instead. You
could develop and debug the e-mail code a lot faster, and the end-user could
then use her e-mail program to assign tasks, forward the e-mail, and so on.

Q:
Where can I find out more about remoting?

A:
The best newsgroup for this is the
microsoft.public.dotnet.framework.remoting group. Also, the MSDN area on the
Microsoft site often publishes articles on aspects of .NET remoting.