[
Advertise | Submit Code | About us | Contact us | Link us
]
Go!
Membership Services
Login
Register

Home
C# General

General

C# Language

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting
C# Windows Forms

General

Combo and List boxes

Miscellaneous Controls

Button Controls

Edit Controls
Cutting Edge

ASP.NET 2.0

Visual Studio 2005

Windows Longhorn

SQL Server 2005
C# Multimedia and GDI+

General

DirectX

GDI+

Audio
Internet & Web

General

Images and multimedia

Database

Utilities

Security

ASP.NET Controls

Design and Architecture

Webservices
.NET

General

Design & Architecture

Algorithms

Database

Security

Active Directory

COM Interop

Remoting

ADO.NET

XML.NET

Tools

Enterprise

IDE
Visual Basic .NET

VB.NET General

VB.NET Controls
General Reading

.NET Books Review

Product Showcase

Book Chapters

Business Design & Strategy
Community

Discuss

Job Board

Discussion

CodeXchange
DeveloperLand

Advertise

Submit Code

About us

Contact us

Link us
Miscellaneous

Favorite Links

Downloads

Programming Sites

Top Stories
Regular Expressions

E-Mail

Date/Time
Home > C# General > Remoting
Going Remote - Part I
Posted by on Tuesday, November 02, 2004 (EST)

In this article, I'm going to walk through a basic Remoting scenario and use it for a more in depth discussion on building a large scale distributed solution.

This article has been viewed: 3,375 times
Technology: Remoting.

In this article, I'm going to walk through a basic Remoting scenario and use it for a more in depth discussion on building a large scale distributed solution.  When I first started learning about Remoting I fell upon a few stumbling blocks and pretty much just wrote it off.  "I can just use Web Services so why go through the drudgery of Remoting" was the excuse I used to not dig into it.  Today, I can hardly imagine programming without it.  There are a few gotchas that I encountered along the way that seemed 'really difficult' at the time which in retrospect, were so trivial that I'm hesitant to acknowledge them.  But, I've become such an avowed Remoting nut that hopefully I can either talk you into using it or convince you it's not as hard as it looks.

A few years ago when I was in college, Client/Server was the big thing (well, it was more than a few years ago but we don't need to go there).  C/S solutions were already fairly common and the Computer Science department was doing its best to prepare us for the world out there.  In one of my senior level classes we started playing with an Oracle db.  It was heaven compared to the lame text file stuff I was so accustomed to.  So I decided that I really liked database programming and started branching out on my own into Oracle Forms and Oracle Reports.  At that time, all of the Oracle books I bought were preaching about the virtues of N-Tier vs Client Server architecture.  It was pretty intuitive considering that in school we did mostly C++ work.  After all, if we decouple Header files from the implementation files, why not use the same metaphor on a larger scale.  So there was this notion of a Presentation Layer, A Business Tier and a Data Access Tier.  It all made perfect sense.  Then I got a job working in VB development and I was stoked to really give this N-Tier thing a try.  In my interview the N-Tier thing was dropped about 15 times so I was pretty excited.  The manager stressed over and over how important it was to decouple the business logic from the presentation logic and the data access logic from the business logic.

What I encountered however was probably rather typical.  "Decoupling" meant building a .dll for the business logic, another one for the data access logic and hard referencing them into a Winforms application.  Sure, this was certainly a lot better architecturally than say, putting all the code behind a few Winforms but what did it really buy us?  Well, for one thing it meant that we could replace a .dll without disturbing the rest of the application so it afforded us some flexibility.  But other than that, it didn't get us much.  The client machines STILL NEEDED THOSE DLL"S and the project still had hard coded references to them in the build.

A while later I took another job and things were essentially the same although there were a few hot shots there that were all about DCOM.  It looked really cool but since I was only doing VB development for about 2 years at that point the response was "NO DCOM FOR YOU!".  Well, that's not entirely true but most of what I got to do was really painful and was essentially following a set of very specific instructions.  No real development on my part.  To be honest, my first foray into DCOM and DNA was about as anticlimactic as it could have been.  Anyone that could type could have done what I did.

So how are things different now?  Well, today you have two pretty effective ways of handling your communications layer - Web Services and Remoting.  For a while I thought Web Services were easier to use than Remoting but that's not really true.  It's true inasmuch as it's easier to get a web service up and running because Remoting configuration is a bit fussy, but after that it's a different story.  And web services are great for many things but they aren't an effective mechanism for many things you may need to do.  And do you really want to call a web service on an in house program when you can hit the database directly, just so you can have a more scalable solution?  Not if performance is a critical factor.  This is where Remoting is worth its weight in gold.  You see, Web Services rely on Remoting themselves, so by using remoting directly you can cut out a large layer of fat.

Enough background though, let's get started.

In a typical client server solution, you may have a Winforms application, a business layer and a data access layer.  These layers are often times just a fancy way of saying a DLL Reference.  As such, the business objects are running on the same box as the UI.  Anyway, to keep things simple, lets say we have an application called KDNClient and a business layer called KDNBusiness.  You add a reference to KDNBusiness to your KDNClient assembly and now you can reference everything in KDNBusiness just like it was any other object.

So how does a remoting scenario differ?  Well, there are many permutations but let's go through a simple one.  In this instance, we're going to make a very simple console appliction called KDNServer.  Now, we're also going to have the same client we otherwise would have, KDNClient.  So where does KDNBusiness exist?  Well, it's going to exist on the Server.  In this particular example I'm going to host them on the same box.  KDNBusiness has two methods .GetAssemblyName() and GetCurrentTime().  I know it's a useless class but I wanted to keep this simple because getting started with Remoting is often the most challenging part.  In the next article we're going to add a Data Access layer to this, which can run on a different machine than both KDNServer and KDNClient. Now, to facilitate the separation of tiers that I mentioned eariler, I'm going to create another assembly (KDNBusinessInterfaces) with one Interface IBusinessInterface (I know, I violated the naming conventions, my bad).  IBusinessInterface defines two methods:  GetAssemblyName() and .GetCurrentTime().  Below is a picture of the actual solution I used to build this project which proves that there is NO Reference to KDNBusiness:

Figure 1.1 - References for KDNClient


Now, all I have so far is a client application with a reference to an assembly that contains an interface in it.  I'll need somewhere to host the business assemblies though and in this case I'm going to create a console application that does only one thing - calls the static RemotingConfiguration.Configure method passing in the name of the configuration file.  Since this is a console application, all you need to do to add the configuration file is select Project ->  Add New Item, then select Application Configuration.  It will automatically add this for you and when you compile, it will be named AssemblyName.exe.config.  As an aside, Configuration files are EXTREMELY Powerful and allow you to do things very simply that would otherwise be a real pain in the butt.  You can handle the configuration through code instead of using a .Config file, but in this scenario it would be really shortsighted.  I say that because if you wanted to move the DALC layer for instance to another server or change any of the remoting configuration info, add a new type or anything else, you'd need to recompile and redeploy.  That is very cumbersome as opposed to merely doing some minor editing in a text editor.  To prove this point, I'm actually going to add the information needed for the DALC Layer that I'm going to discuss in the next article.  As such, I will be able to add an entire tier, running on another machine, without touching one line of anything in the server application.

Since this is a Console application, I need to add in one line of code (Console.ReadLine()) so that the application stays running.  Other than that only one line is needed to make a "Remoting Server" as illustrated below (yes, this code actually works and that's all there is to it):

Figure 1.2 - KDNRemoteServer
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Module Module1
  Sub Main()
    RemotingConfiguration.Configure("KDNRemoteserver.exe.config")
    Console.WriteLine("KDN Remoting Server (Running)")
    Console.ReadLine()
    Console.WriteLine("Closing down server")
  End Sub
End Module

Also, note that this part is written in VB.NET.  I did this on purpose because the rest of the application is written in C# and I wanted to illustrate that you can easily mix and match languages and still do whatever you need.  

I created another assembly in the same solution, KDNBusinessAPI which will hold the Business Object:  The entire class definition is provided below.  Note that although this is a really simple class, it can get much more complex and in part II of this article, I'll have a much more complex example:

Figure 1.3 - KDNBusinessAPI
using System;
using KDN.BusinessLayer.Business.Interfaces;
namespace KDN.BusinessLayer.Business
{
   /// 
  /// Summary description for Class1.
  /// 
  public class Business : MarshalByRefObject, IBusinessInterface
   {
      static Business(){}
      public System.String GetAssemblyName()
      {
         return System.Reflection.Assembly.GetExecutingAssembly().FullName;
      }
      public System.DateTime GetCurrentTime()
      {
         return DateTime.Now;
      }
   }
}
Now, I added a reference to the KDNBusinessAPIInterfaces assembly which is sitting in an entirely different solution and the only reason I need it is because Business implements IBusinessInterface.  The next part is what gave me the most trouble when I was first learning remoting - the actual implementation of the configuration file:

Figure 1.4 - KDNRemoteServer.exe.config
 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.remoting>
     <application>
        <channels>
           <channel ref="http" port="8010" />
        </channels>
        <service>
           <wellknown mode="Singleton"
              type="KDN.BusinessLayer.Business.Business, KDNBusinessAPI"
              objectUri="Business.soap"/>
           <wellknown mode="Singleton"
              type="KDN.DataAccessLayer.BusinessDALC, KDNBusinessDALC"
              objectUri="BusinessDALC.soap"/>
        </service>        
      
</application>  
  
</system.runtime.remoting>
</configuration>

Let's go through this line by line.  The first thing I do is add a reference to system.runtime.remoting.  I specify a channel to listen on and picked 8010 randomly.  You can use any open port but you don't want to use one that's being used by other applications.  I specify that this will run as HTTP, but I can easily have modified this to be TCP by simply typing TCP over HTTP.  We're using Server Activated objects exclusively in this part and we're going to choose 'Singleton' as the mode.  This means that the object will actually be created whenever the first time something calls one of its methods, not when its instantiated.  There are other modes to run server activated objects (SAO) and there are also Client Activated objects but I'll explain the nuances as I go along.  Now, the type needs to be the FULLY QUALIFIED Object name.  I goofed up with my namespaces and put it in KDN.BusinessLayer.Business so it's full name is KDN.BusinessLayer.Business.Business.  Then you reference the assembly name, in this instance KDNBusinessAPI.  Note that next to this i'm creating the DALC Class.  Tomorrow, I'm going to show you how to have the Business Object call the DALC remotely so that the Client Program will only have a reference to IBusinessDALC which will do all of the data access work.  Once this change is made, Business will never touch the Database.  This implementation will give you a lot of flexibility in terms of performance, security and flexibility.  And as I mentioned eariler, I won't have to change one line of code in the server to facilitate this.  

Once this is in place, all you need to do is use this line:  
RemotingConfiguration.Configure("KDNRemoteserver.exe.config") in your console app and you're ready to remotely invoke your application.  Just to make sure you're with me, the server application has a reference to the KDNBusinessAPI assembly which has a reference to the KDNBusinessAPIInterfaces.  The client app only has a reference to the KDNBusinessAPIInterfaces assembly.

Also, it's worth mentioning that you can create the server in other forms, and IIS is another very easy way to go about it.  For now though, suffice to say that  a simple console app (2 lines of code minimum) or equally simple Winforms app will do the job.  And running under IIS is just as simple!

Now, what does the client code look like?

Figure 1.5  - IBusinessInterface
using System;
namespace KDN.BusinessLayer.Business.Interfaces
{
   public interface IBusinessInterface
   {
        System.String GetAssemblyName();   
        System.DateTime GetCurrentTime();
   }
}

Notice that the method definitions match those of Business, because, well, Business implements this Interface ;-).

Ok, now for the actual code that 'Remotes' everything::

Figure 1.6 - KDNClient
private void button1_Click(object sender, System.EventArgs e)
{
    HttpChannel kdnChannel = new HttpChannel();
    ChannelServices.RegisterChannel(kdnChannel);         
   object BusinessInstance = Activator.GetObject(typeof(IBusinessInterface), 
 "http://localhost:8010/Business.soap");
    IBusinessInterface bus = (IBusinessInterface) BusinessInstance;
    rtb.Text += "Remote Assembly:  " + 
  bus.GetAssemblyName() + "\r\n";
    rtb.Text += "Current Assembly:  " + 
  System.Reflection.Assembly.GetExecutingAssembly().FullName;
    rtb.Text += "\r\n " + "Current Time:  " + 
  bus.GetCurrentTime().ToString();
}

All I have is a Form (Form1.cs) with a Button on it (button1) and a RichTextBox (rtb).  Remember again that I DO NOT HAVE  A REFERENCE TO BUSINESS in this assembly, but since I have a reference to IBusinessInterface, thanks to the wonders of Remoting, I can do anything I would otherwise be able to do if the reference was hard coded.  Of course if you have something additional in Business that's not implemented in the Interface you're out of gas - but that's why the class should implement the interface after all.

So what just happened here?  First, we register a HTTPChannel [Remoting scenario and use it for a more in depth discussion on building a large scale distributed solution.  When I first started learning about Remoting I fell upon a few stumbling blocks and pretty much just wrote it off.  "I can just use Web Services so why go through the drudgery of Remoting" was the excuse I used to not dig into it.  Today, I can hardly imagine programming without it.  There are a few gotchas that I encountered along the way that seemed 'really difficult' at the time which in retrospect, were so trivial that I'm hesitant to acknowledge them.  But, I've become such an avowed Remoting nut that hopefully I can either talk you into using it or convince you it's not as hard as it looks.

A few years ago when I was in college, Client/Server was the big thing (well, it was more than a few years ago but we don't need to go there).  C/S solutions were already fairly common and the Computer Science department was doing its best to prepare us for the world out there.  In one of my senior level classes we started playing with an Oracle db.  It was heaven compared to the lame text file stuff I was so accustomed to.  So I decided that I really liked database programming and started branching out on my own into Oracle Forms and Oracle Reports.  At that time, all of the Oracle books I bought were preaching about the virtues of N-Tier vs Client Server architecture.  It was pretty intuitive considering that in school we did mostly C++ work.  After all, if we decouple Header files from the implementation files, why not use the same metaphor on a larger scale.  So there was this notion of a Presentation Layer, A Business Tier and a Data Access Tier.  It all made perfect sense.  Then I got a job working in VB development and I was stoked to really give this N-Tier thing a try.  In my interview the N-Tier thing was dropped about 15 times so I was pretty excited.  The manager stressed over and over how important it was to decouple the business logic from the presentation logic and the data access logic from the business logic.

What I encountered however was probably rather typical.  "Decoupling" meant building a .dll for the business logic, another one for the data access logic and hard referencing them into a Winforms application.  Sure, this was certainly a lot better architecturally than say, putting all the code behind a few Winforms but what did it really buy us?  Well, for one thing it meant that we could replace a .dll without disturbing the rest of the application so it afforded us some flexibility.  But other than that, it didn't get us much.  The client machines STILL NEEDED THOSE DLL"S and the project still had hard coded references to them in the build.

A while later I took another job and things were essentially the same although there were a few hot shots there that were all about DCOM.  It looked really cool but since I was only doing VB development for about 2 years at that point the response was "NO DCOM FOR YOU!".  Well, that's not entirely true but most of what I got to do was really painful and was essentially following a set of very specific instructions.  No real development on my part.  To be honest, my first foray into DCOM and DNA was about as anticlimactic as it could have been.  Anyone that could type could have done what I did.

So how are things different now?  Well, today you have two pretty effective ways of handling your communications layer - Web Services and Remoting.  For a while I thought Web Services were easier to use than Remoting but that's not really true.  It's true inasmuch as it's easier to get a web service up and running because Remoting configuration is a bit fussy, but after that it's a different story.  And web services are great for many things but they aren't an effective mechanism for many things you may need to do.  And do you really want to call a web service on an in house program when you can hit the database directly, just so you can have a more scalable solution?  Not if performance is a critical factor.  This is where Remoting is worth its weight in gold.  You see, Web Services rely on Remoting themselves, so by using remoting directly you can cut out a large layer of fat.

Enough background though, let's get started.

In a typical client server solution, you may have a Winforms application, a business layer and a data access layer.  These layers are often times just a fancy way of saying a DLL Reference.  As such, the business objects are running on the same box as the UI.  Anyway, to keep things simple, lets say we have an application called KDNClient and a business layer called KDNBusiness.  You add a reference to KDNBusiness to your KDNClient assembly and now you can reference everything in KDNBusiness just like it was any other object.

So how does a remoting scenario differ?  Well, there are many permutations but let's go through a simple one.  In this instance, we're going to make a very simple console appliction called KDNServer.  Now, we're also going to have the same client we otherwise would have, KDNClient.  So where does KDNBusiness exist?  Well, it's going to exist on the Server.  In this particular example I'm going to host them on the same box.  KDNBusiness has two methods .GetAssemblyName() and GetCurrentTime().  I know it's a useless class but I wanted to keep this simple because getting started with Remoting is often the most challenging part.  In the next article we're going to add a Data Access layer to this, which can run on a different machine than both KDNServer and KDNClient. Now, to facilitate the separation of tiers that I mentioned eariler, I'm going to create another assembly (KDNBusinessInterfaces) with one Interface IBusinessInterface (I know, I violated the naming conventions, my bad).  IBusinessInterface defines two methods:  GetAssemblyName() and .GetCurrentTime().  Below is a picture of the actual solution I used to build this project which proves that there is NO Reference to KDNBusiness:

Figure 1.1 - References for KDNClient

Now, all I have so far is a client application with a reference to an assembly that contains an interface in it.  I'll need somewhere to host the business assemblies though and in this case I'm going to create a console application that does only one thing - calls the static RemotingConfiguration.Configure method passing in the name of the configuration file.  Since this is a console application, all you need to do to add the configuration file is select Project ->  Add New Item, then select Application Configuration.  It will automatically add this for you and when you compile, it will be named AssemblyName.exe.config.  As an aside, Configuration files are EXTREMELY Powerful and allow you to do things very simply that would otherwise be a real pain in the butt.  You can handle the configuration through code instead of using a .Config file, but in this scenario it would be really shortsighted.  I say that because if you wanted to move the DALC layer for instance to another server or change any of the remoting configuration info, add a new type or anything else, you'd need to recompile and redeploy.  That is very cumbersome as opposed to merely doing some minor editing in a text editor.  To prove this point, I'm actually going to add the information needed for the DALC Layer that I'm going to discuss in the next article.  As such, I will be able to add an entire tier, running on another machine, without touching one line of anything in the server application.

Since this is a Console application, I need to add in one line of code (Console.ReadLine()) so that the application stays running.  Other than that only one line is needed to make a "Remoting Server" as illustrated below (yes, this code actually works and that's all there is to it):

Figure 1.2 - KDNRemoteServer
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Module Module1
  Sub Main()
    RemotingConfiguration.Configure("KDNRemoteserver.exe.config")
    Console.WriteLine("KDN Remoting Server (Running)")
    Console.ReadLine()
    Console.WriteLine("Closing down server")
  End Sub
End Module

Also, note that this part is written in VB.NET.  I did this on purpose because the rest of the application is written in C# and I wanted to illustrate that you can easily mix and match languages and still do whatever you need.  

I created another assembly in the same solution, KDNBusinessAPI which will hold the Business Object:  The entire class definition is provided below.  Note that although this is a really simple class, it can get much more complex and in part II of this article, I'll have a much more complex example:

Figure 1.3 - KDNBusinessAPI
using System;
using KDN.BusinessLayer.Business.Interfaces;
namespace KDN.BusinessLayer.Business
{
   /// 
  /// Summary description for Class1.
  /// 
  public class Business : MarshalByRefObject, IBusinessInterface
   {
      static Business(){}
      public System.String GetAssemblyName()
      {
         return System.Reflection.Assembly.GetExecutingAssembly().FullName;
      }
      public System.DateTime GetCurrentTime()
      {
         return DateTime.Now;
      }
   }
}
Now, I added a reference to the KDNBusinessAPIInterfaces assembly which is sitting in an entirely different solution and the only reason I need it is because Business implements IBusinessInterface.  The next part is what gave me the most trouble when I was first learning remoting - the actual implementation of the configuration file:

Figure 1.4 - KDNRemoteServer.exe.config^], which has three overloads and can easily specify a different port.  We told our server application to serve up instances of the class via port 8010 as Business.soap (you can also use REM but if you want to use IIS, you need to use one of the two).  We used the static GetObject method of the Activator class and cast the returned object to match the type of the interface.  Since our object implements this interface, we know any method we can see in the interface will have a matching definition with the object.  The next three calls simply show you how you'd use it - JUST LIKE ANY OTHER OBJECT.

The resulting output is the following:

Figure 1.7 - Output from Button1_Click

Remote Assembly:  KDNBusinessAPI, Version=1.0.1760.38181, Culture=neutral, PublicKeyToken=null
Current Assembly:  KDNClient, Version=1.0.1760.41298, Culture=neutral, PublicKeyToken=null
Current Time:  10/26/2004 11:57:34 PM

There you have it, proof that they are running in different assemblies and across AppDomain boundaries.  Now, let's say I wanted to change the configuration so it runs on a machine named Bill.  All I'd need to do is change LocalHost in the URL above to Bill, move the server program over to Bill, start it, and viola, my business logic is running on a different machine.

Think about how cool this really is.  Let's say you have a web application and realize that you don't want your IIS machine to have access to your DB directly.  Move the Business and/or Dalc layers inside  a DMZ, leave the web server outside of the DMZ, have the web server call the remote assemblies, have the remote assemblies talk to your database, and by simply changing one or two URL references and copying a few files, you just completely closed access to your database from the rest of the world.  In order to get to it, they'd have to successfully hack your web app, find out where the remoted assemblies were, hack those (which are behind a firewall) and even then, chances are your assemblies aren't doing anything that risky anyway.

Well, I wanted to get through a  very basic scenario before I moved onto bigger things. I'm going to have to spend some time discussing Client Activated objects because they behave MUCH differently than Server Activated objects. I also need to touch up the behavior of statics in remoting scenarios.  Then I'll move on to hosting in IIS and a few other cool tricks.  Think about this as well, in under 25 lines of code, we just built an application written in two languages which can run on two different machines and can be moved around by changing a machine  name in a URL.  It's not too difficult to imagine what kind of power you can wield using Remoting.

Top Go to Table of Contents

About DeveloperLand Administrator

Click here if you want to know more about .

Other articles that may interest you

  • Write a Word Add-In – Part 0
  • Write a Word Add-In – Part I
  • Lengthy Operations on Single Thread in .NET Application
  • Learning Draughts
  • Exceptions and Performance
  • Average Rating :

    Discussion Forums
    Got a programming related question? Hopefully someone has the answer... Want to help out other developers? Visit our discussion forums.

    Sponsored by:

    New Articles

  • Exceptions and Performance
    Almost every time exceptions are mentioned in mailing lists and newsgroups, people say they're really expensive.Let's examine that claim, shall we?

  • Creating multilingual websites - Part 1
    Extend the existing globalization capabilities of .NET to create flexible and powerful multilingual web sites. First, create a custom ResourceManager, and then create custom localized-capable server controls to easily deploy multilingual functionality.

  • Parameter passing in C#
    Many people have become fairly confused about how parameters are passed in C#, particularly with regard to reference types. This page should help to clear up some of that confusion

  • Most Popular Articles

  • LDAP, IIS and WinNT Directory Services
    This article explains how to use .NET Directory Services to retrieve and search directory objects, create new directory objects and edit or delete existing directory objects. Describ