tags:

views:

2152

answers:

5

I am trying to migrate my .net remoting code to wcf but I'm finding it difficult. Can someone help me migrate this simple Remoting based program below to use WCF? The program implements a simple publisher/subscriber pattern where we have a single TemperatureProviderProgram that publishers to many TemperatureSubcriberPrograms that subcribe to the TemperatureProvider.

To run the programs:

  1. Copy the TemperatureProviderProgram and TemperatureSubcriberProgram into seperate console application projects.
  2. Copying to remaining classes and interfaces into a common Class Library project then add a reference to System.Runtime.Remoting library
  3. Add a reference to the Class Library project from the console app projects.
  4. Complie and run 1 TemperatureProviderProgram and multiple TemperatureSubcriberProgram.

Please note no IIS or xml should be used. Thanks in advance.

public interface ITemperatureProvider
{
    void Subcribe(ObjRef temperatureSubcriber);
}

[Serializable]
public sealed class TemperatureProvider : MarshalByRefObject, ITemperatureProvider
{
    private readonly List<ITemperatureSubcriber> _temperatureSubcribers = new List<ITemperatureSubcriber>();
    private readonly Random randomTemperature = new Random();

    public void Subcribe(ObjRef temperatureSubcriber)
    {
        ITemperatureSubcriber tempSubcriber = (ITemperatureSubcriber)RemotingServices.Unmarshal(temperatureSubcriber);
        lock (_temperatureSubcribers)
        {
            _temperatureSubcribers.Add(tempSubcriber);
        }
    }

    public void Start()
    {
        Console.WriteLine("TemperatureProvider started...");
        BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
        provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
        TcpServerChannel tcpChannel = new TcpServerChannel("TemperatureProviderChannel", 5001, provider);
        ChannelServices.RegisterChannel(tcpChannel, false);
        RemotingServices.Marshal(this, "TemperatureProvider", typeof(ITemperatureProvider));

        while (true)
        {
            double nextTemp = randomTemperature.NextDouble();

            lock (_temperatureSubcribers)
            {
                foreach (var item in _temperatureSubcribers)
                {
                    try
                    {
                        item.OnTemperature(nextTemp);
                    }
                    catch (SocketException)
                    {}
                    catch(RemotingException)
                    {}
                }
            }
            Thread.Sleep(200);
        }
    }
}

public interface ITemperatureSubcriber
{
    void OnTemperature(double temperature);
}

[Serializable]
public sealed class TemperatureSubcriber : MarshalByRefObject, ITemperatureSubcriber
{
    private ObjRef _clientRef;
    private readonly Random portGen = new Random();

    public void OnTemperature(double temperature)
    {
        Console.WriteLine(temperature);
    }
    public override object InitializeLifetimeService()
    {
        return null;
    }

    public void Start()
    {
        BinaryServerFormatterSinkProvider provider = new BinaryServerFormatterSinkProvider();
        provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

        int port = portGen.Next(1, 65535);
        TcpServerChannel tcpChannel = new TcpServerChannel(string.Format("TemperatureSubcriber_{0}", Guid.NewGuid()), port, provider);
        ChannelServices.RegisterChannel(tcpChannel, false);

        ITemperatureProvider p1 = (ITemperatureProvider)RemotingServices.Connect(typeof(ITemperatureProvider), "tcp://localhost:5001/TemperatureProvider");
        _clientRef = RemotingServices.Marshal(this, string.Format("TemperatureSubcriber_{0}_{1}.rem", Environment.MachineName, Guid.NewGuid()));
        p1.Subcribe(_clientRef);
    }
}

public class TemperatureProviderProgram
{
    static void Main(string[] args)
    {
        TemperatureProvider tp = new TemperatureProvider();
        tp.Start();
    }
}
public class TemperatureSubcriberProgram
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press any key to start TemperatureSubcriber.");
        Console.ReadLine();
        TemperatureSubcriber ts = new TemperatureSubcriber();
        ts.Start();
        Console.ReadLine();
    }
}
+1  A: 

You will need to modify your logic a bit. If you want to migrate this app to WCF. You will need to have clients pull data from the service at regular intervals.

You will also need a Windows service or application to host the WFC like the console you are using in the previous code.

Alexandre Brisebois
Actually, WCF does support duplex for the server pushing to the client. I wouldn't us it myself, however.
Marc Gravell
true, but i'm not sure if its the best model to use for something like this. I don't know about the volume of clients this is supposed to support.
Alexandre Brisebois
WCF does have duplux channels, but they have lots of problem, so use polling if you can get away with it.
Ian Ringrose
+1  A: 

In WCF, with a "push" from the server you're really talking about duplex comms; the MarshalByRefObject is largely redundant here (AFAIK). The page here discusses various scenarios, including duplex/callbacks.

If the issue is xml (for some philosophical reason), then simply using NetDataContractSerializer rather than DataContractSerializer might help.

The other approach is to have the clients "pull" data periodically; this works well if you need to support basic http, etc.

Marc Gravell
A: 

Well I build real time systems so polling is not an option - I need to push data.

Also I am finding there is no WCF equivalent of System.Runtime.Remoting.ObjRef! This is an extremely useful type that encapsulates a service endpoint and can be serialise and passed around the network to other remoting service.

Think I’ll be sticking with good old remoting until the ObjRef equivalent is introduced.

+1  A: 

What it sounds like you want to do is use WCF NetTcpBinding with Callbacks.

Take a look at this: http://www.codeproject.com/KB/WCF/publisher_subscriber.aspx

"Learning WCF" by Michele Bustamante is also very good. You can get Chpt1 for VS2008 at her website along with the code for the book. Chpt1 will explain/demo setting up connections and such. She also has downloadable sample code. One of the Samples is a DuplexPublishSubscribe.

A: 

Yes it is true, just one correction.. ObjRefs are created automatically when any MarshalByRefObject derived object is going outside the appdomain. So in this case your ITemperatureProvider interface Subscribe method shoud take ITemperatureSubscriber instead of objref. And then on client side just call p1.Subscribe(this) and the remoting layer will generate ObjRef from the object that will be serialized and sent. (sending b reference)