tags:

views:

1878

answers:

3

In .NET remoting what is the difference between RemotingConfiguration.RegisterWellKnownServiceType and RemotingServices.Marshal?

What I want to do is create an object in a Windows Service, then put it in as a remoting object and have the Windows Service and the Client both act on the remoting object.

I thought the below code would accomplish this.

FooRemoting foo = new FooRemoting();

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), serverName, WellKnownObjectMode.Singleton);
RemotingServices.Marshal(foo);
+5  A: 

This is what I found.

RemotingConfiguration.RegisterWellKnownServiceType(typeof(FooRemoting), 
          serverName, WellKnownObjectMode.Singleton);

RegisterWellKnownServiceType will create the object and make it a Singleton to any client that consumes it, but a reference by the server is not created. The object is not created until a client ask for it, and the same object is used for any other clients.

RemotingServices.Marshal(foo);

Marshal will register an object that has been created by the server, in this case a windows service. Then server will then have reference to the object and the clients will consume the same object.

My issue was using the Marshal to register the remoting object. Over time the remoting object will disappear for clients to consume, i.e. no longer on the remoting object. The service would still keep its reference. Then I tried the RegisterWellKnownServiceType and the clients keep getting the correct reference, however I could not get the service to have a reference to the same object.

The solution is overriding the remoting object in this case FooRemoting. If I overrode the InitializeLifetimeService and returned null, the client would never lose connection, and the service will, keep the connection.

public override object InitializeLifetimeService()
{
    //return base.InitializeLifetimeService();
    return null;
}

In order to keep the object created by the service and have the client to use the same object you must use

RemotingServices.Marshal(foo);

and override InitializeLifetimeService to return null.

David Basarab
A: 

I did one experiment with RemotingServices.Marshal like this

Remotable component hosted in a Windows Exe. Exe code is

Form1_Load(object sender, EventArgs e)
{
   RemotingConfiguration.Configure("path of the config file");
   RemoteClass obj = new RemoteClass();
   obj.MyVal =100;

   RemotingServices.Marshal(obj);
}


public RemoteClass: MarshalByRefObj
{
   static int Counter;
   public RemoteClass()
   {
      Counter++;
   }

   int _MyVal =0;
  public int MyVal
 {
    get
   {
      return _MyVal;
   }
   set
   {
      _MyVal = value;
   }
 }       
}

Now in the client side code

button1_click()
{
  RemoteClass obj = Activator.GetObject(typeof(RemoteClass), "object URI");
  if(RemotingServices.IsTransparentProxy(obj))
  {
      MessageBox.Show(obj.Myval.ToString());
  }
}

It will popup the message as 0 not 100. If you put a breakpoint in the constructor of RemoteClass, you will see that the constructor is getting called 2 times

  1. When the RemoteClass object is created in the Service itself
  2. When the client is making call to MyVal property.

I think RemotingServices.Marshal has nothing to do with the single instance. Even if you use just RemotingConfiguration.Configure and override the InitializeLifetimeService so that it will return null, will be sufficient to host a remotable component.

+2  A: 

It is possible to expose MarshalByRefObjects which have parameterful constructors over remoting, and it's possible for users of the class to only deal with its interface.

I have created a small proof of concept project. It has 3 projects: Server, Client, and Core. Server and Client both reference Core but do not reference each other.

In core, we define a service interface:

namespace Core
{
    public interface ICountingService
    {
        int Increment();
    }
}

The server defines the concrete implementation, which the client doesn't have a reference to:

namespace Server
{
    public class CountingService : MarshalByRefObject, ICountingService
    {
        private static int _value = 0;

        public CountingService(int startValue)
        {
            _value = startValue;
        }

        public int Increment()
        { // not threadsafe!
            _value++;
            return _value;
        }
    }
}

The important bits to note are that it has a constructor with a parameter, it is a MarshalByRefObject, and it implements the interface in the core project.

The server project is a console app which sets up a remoting channel (arbitrarily over HTTP for this example), creates the service, and registers it with remoting:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpServerChannel serverChannel = new HttpServerChannel(8234);
            ChannelServices.RegisterChannel(serverChannel, false);

            // Following line won't work at runtime as there is no parameterless constructor
            //RemotingConfiguration.RegisterWellKnownServiceType(typeof(CountingService),
            //                     "CountingService.rem", WellKnownObjectMode.Singleton);

            CountingService countingService = new CountingService(5);
            RemotingServices.Marshal(countingService, "CountingService.rem");

            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
    }
}

The above code has registered the URL http://localhost:8234/CountingService.rem which holds the instantiated service, which will start counting from 5.

The client, also a console app, can then get a reference, using the interface class:

using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using Core;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            HttpClientChannel serverChannel = new HttpClientChannel();
            ChannelServices.RegisterChannel(serverChannel, false);

            for (int i = 0; i < 5; i++)
            {
                ICountingService countingService =
                    (ICountingService)Activator.GetObject(typeof(ICountingService),
                    "http://localhost:8234/CountingService.rem");

                int newValue = countingService.Increment();
                Console.WriteLine("Value is " + newValue);
            }

            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
    }
}

When the server and client are run, it prints values from 6 to 10.

Summary: client knows only about the interface; implementation constructor can have parameters; instantiation can be controlled by your own code rather than by .NET. Very useful when dealing with constructor-based dependency injection with remoting objects.

Daniel Flower