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.