Using Interfaces In .NET Remoting
By
David Talbot, 01/14/02
Abstract
.NET Remoting allows for a clean separation between client side code and server
side code through the use of interfaces. This allows a developer to distribute
generic interfaces to client machines and change the server side code without
having to redistribute the code changes back to the clients.
In this article, we will create a remote object and reference it only by
interface. We will also create an interfaces DLL that both the client and
server code use.
Step 1: Creating The Interface Library
Launch Visual Studio.NET Beta 2, then choose File->New->Project. Choose to
create a new "C# Library" and name it RemotingExampleInterfaces then click on
OK. The purpose for this library is to declare the object interfaces that we
will use on both the client and server side.
The code for this library is very simple, but a few notes need to be placed on
the code below.
First, instead of declaring classes we are declaring interfaces.
This tells the compiler that we're not actually creating a class, we are
creating a "template" for a class. An interface defines what Methods the object
should have as well as the parameters and return values for these methods. Note
the ';' at the end of each method declaration instead of the '{}'s that would
normally contain the method's implementation. The final thing to mention on
interfaces as that they have no access modifiers such as public or private.
That is defined by the class that we choose to implement our interface in.
using System;
namespace RemotingExampleInterfaces
{
public interface IResume
{
String GetformattedResume();
String GetformattedName();
}
public interface IRemotingExampleService
{
IResume Getresume();
String GetformattedResume();
}
}
|
Choose Build from the Build menu to compile this DLL. The output will be placed
in the bin/Debug subdirectory of you project directory.
Step 2: Create The Implementation And Server Object
Now that we have defined the interfaces we plan on using, we need to create an
implementation of these interfaces that does something. In a real project, you
would create another shared library with you implementation to aid code reuse
and keep your project modular. In this simple example, we'll just create a
console application that provides an inline implementation of our interfaces.
Go to File->New->Project and choose C# Console application, then click on
OK.
Since we are making use of the System.Runtime.Remoting namespace, we will need
to add a reference to the correct DLL. This is done by clicking Project->Add
Reference, click on the ".NET" tab and chose System.Runtime.Remoting.dll.
We also need to add a reference to the interfaces DLL we created in Step 1. This
is done by choosing Project->Add Reference, click "Browse" and choose the
.DLL file in the bin/Debug directory of the RemotingExampleInterfaces project
we created in Step 1.
public class Resume : MarshalByRefObject, IResume
public class RemotingExampleService : MarshalByRefObject, IRemotingExampleService
|
Take a look at these two class declarations. Each is a subclass of the
MarshalByRefObject type. This is very important to the use of these
objects in .NET remoting. Also, each of these objects is an implementation of
the IResume and IRemotingExampleService interfaces we created in Step 1. Note
that in these classes are methods that match the names of the methods we
defined in our interface.
If you have questions about the TcpServerChannel or related topics, check out my
article entitled, "Introduction
to .NET Remoting" hosted on this site.
The complete code for the object is below.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using RemotingExampleInterfaces;
using System.Runtime.Serialization;
namespace RemotingInterfaceServer
{
public class EntryPoint
{
public static void Main(string[] args)
{
TcpServerChannel channel = new TcpServerChannel(9988);
ChannelServices.RegisterChannel(channel);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingExampleService),
"RemotingExampleService", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("press Any Key");
System.Console.ReadLine();
}
}
public class RemotingExampleService : MarshalByRefObject, IRemotingExampleService
{
public RemotingExampleService() {}
public IResume Getresume()
{
Resume resume = new Resume();
resume.FirstName="David";
resume.LastName="Talbot";
resume.Title="Senior Software Architect";
resume.Body="Did some cool stuff";
return (IResume)resume;
}
public String GetformattedResume()
{
return Getresume().GetformattedResume();
}
}
[Serializable]
public class Resume : MarshalByRefObject, IResume
{
public String FirstName, LastName, Body, Title;
public String GetformattedResume()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("*"+ GetformattedName() +"*\n");
sb.Append("--"+ Title +"--\n");
sb.Append("--------------------------\n");
sb.Append(Body);
return sb.ToString();
}
public String GetformattedName()
{
return FirstName +" "+ LastName;
}
}//END OF Resume Object
}
|
Compile this program by going to Build->Build and note the location of the
generated executable file found in the /bin/debug subdirectory of this project.
Step 3: Create The Client
To create the client program, go to File->New->Project and choose a C#
Console Application named RemotingExampleClient.
IRemotingExampleService resService =(IRemotingExampleService)Activator.GetObject(
typeof(IRemotingExampleService),
"tcp://localhost:9988/RemotingExampleService");
Console.WriteLine("RESUME:\n"+ resService.GetformattedResume());
|
Notice that we're using the IRemotingExampleService here instead of
RemotingExampleService? That is because our client side code knows only about
the interface, not the implementation the server is using. The result of this
is the server is serving "RemotingExampleService", but our client is using
"IRemotingExampleService".
IResume aResume = resService.Getresume();
Console.WriteLine("NAME:"+ aResume.GetformattedName());
Console.WriteLine("RESUME:"+ aResume.GetformattedResume());
|
This section of code makes use of the instance of IRemotingExampleService to
return yet another interface. Here we don't have to make any calls to the
Activator object because the .NET runtime accesses the server's Resume
implementation transparently using the IResume interface.
To make this code work, we will need to add a reference to both our
RemotingExampleInterfaces and System.Runtime.Remoting dlls as we did in step 2.
Without this you will get compiler errors.
The complete code listing is below. For more information on the Activator
object, see my article titled "Introduction
to .NET Remoting" on this site.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using RemotingExampleInterfaces;
namespace RemotingExampleClient
{
class RemotingExampleClient
{
static void Main(string[] args)
{
ChannelServices.RegisterChannel(new TcpClientChannel());
IRemotingExampleService resService =(IRemotingExampleService)Activator.GetObject(
typeof(IRemotingExampleService),
"tcp://localhost:9988/RemotingExampleService");
Console.WriteLine("RESUME:\n"+ resService.GetformattedResume());
IResume aResume = resService.Getresume();
Console.WriteLine("NAME:"+ aResume.GetformattedName());
Console.WriteLine("RESUME:"+ aResume.GetformattedResume());
Console.WriteLine("press any key to continue...");
Console.Read();
}//END OF MAIN METHOD
}//END OF RemotingExampleClient class
}//END OF RemotingExampleClient namespace
|
Compile this project and note the location of the .EXE file.
Running the completed example
Run the server application we created in step 2 by double clicking the .exe file
we created. Then run the client application by double clicking the .exe file
created in step 3. If all goes well, you should see the formatted resume
output.
Conclusion
Interfaces are the preferred way to access .NET remote objects. They enable the
developer to create clean code with a complete separation between client side
use of remote objects and the server side implementation. In most cases it is a
good idea to use a DLL for your interfaces, another DLL for your implementation
in order to maximize reuse potential.
| David Talbot is an experienced Software Architect with a diverse background
including creating network appliances, working with television set top boxes,
building Billing/CRM systems, Web Portals and more. He has also provided
technical guidance in different capacities on two C# books.
David is currently finishing up work on a real estate analytics application in
C# for Pathfinder Technologies and seeking additional contract or permanent
work.
|