tags:

views:

78

answers:

3

I've got a WCF service that offers many lists of items of different types. The lists can be changed on the server. Every change has to be published to all clients to make sure every client has an up-to-date copy of each server list.

Currently I'm using this strategy: On login, every client receives the current status of every list. On every change, the added or removed item is sent to all clients using the respective callback method.

The drawback is that I have to create a new callback method for every list, since the items are of different types and cannot be sent using a single callback method. Is there a pattern I could apply? Or do I really have to duplicate code for each of the lists?

Edit: The lists may be changed frequently, so I'd prefer to avoid to send the whole list on each change. Instead, I'm just sending the changed item.

(Edit2: minor clarifiactions)

A: 

You're talking about a classic example of a publish/subscribe pattern - one publisher (your server) creates and publishes new data, and any number of subscribers can get the latest updates.

There's a number of approaches on how to implement that with WCF:

  • callback contracts
  • message queues
  • the Windows Azure service bus in the cloud

Check out some resources for some ideas:

marc_s
I believe this is very close to what I already do. The links you provided do not seem to solve the basic problem - I have to create new Publish/Subscribe methods for each of the lists. There are many lists, so I'd really like to avoid that.
mafutrct
A: 

There is one solution I could think of, but it feels a bit weird and I'm not really confident.

The server offers a generic subscription method for each of the lists. The name of the list is passed as parameter. On a change of any list, the server sends the name of the changed list together with the changed item. The issue is, the item could not be passed usually, because the callback contract has only a single "ListWasChanged" method (I absolutely want to avoid having a new callback method for each of the lists, see question).

However, this can be solved by passing the changed item serialized as string. By using NetDataContractSerializer, the client can easily reconstruct the serialized item and pass it to the correct handler for each list.

What do you think of this? Is this feasible? At first I thought this hurts the performance too badly, but on second thought, we have to serialize each item anyway. The only added performance hit is the serialization of the string, which should not be too much.

Since this may sound a bit confusing, here's an implementation in pseudo code:

server:

// let's call it SyncList - a list that sends updates on changes
class SyncList<T> : List<T>
{
    // this is called on every change of the list
    void OnListChanged<T (string name, T item)
    {
        // serialize the item
        string ser = NetDataContractSerializer.Serialize(item);
        // send the item together with the name of the list
        WcfCallbackChannel.ListChanged (name, ser);
    }
}

client:

// this stores each of the lists
Dictionary<string, List<T>> _Lists;

// the list callback implementation
class WcfCallbackImplementation : IWcfCallbackContract
{
    // called on every change of ANY list
    void ListChanged (string name, string item)
    {
        // get the item back
        var item = NetDataContractSerializer.Deserialize (item);
        // add/remove/update item
        _Lists[name].Add (item);
    }
}
mafutrct
+2  A: 

Based on your answer, provide a method that accepts a string which specifies the name of the list the client would like to subscribe to. Manage the subscriber lists using a Dictionary<string, List<IWcfCallbackContract>>.

The ListChanged callback method should contain three arguments: string listName, ChangeType changeType (where ChangeType is an enumeration specifying whether the item was added, removed or updated) and lastly object changedItem. You will also have to use the ServiceKnownTypeAttribute to specify what types of objects can appear in the changedItem argument.

When an item in a list has changed, get all the subscribers of that list from the Dictionary<> object, and notify each one using the ListChanged method. The client should cast the changedItem from System.Object to whatever type it expects (as per the list specified in the listName argument).

Allon Guralnek
I'm not sure if that works, you did not address the WCF interface which is the actual problem. I believe you can't just transmit any Object.
mafutrct
Yes, you can. That is why I mentioned the `ServiceKnownTypeAttribute` - it has to know what the possible types are that can be serialized in an `object` type, and then it works. Otherwise it will throw a runtime exception.
Allon Guralnek
Indeed. I think this attribute is a good idea, it's not too much of a hassle to add it for every list actually.
mafutrct
You can even use the `ServiceKnownTypeAttribute(String, Type)` overload of it that takes a name of a method that returns a list of types. I use that overload, and in that method I use reflection to find all the classes that are decorated with my own custom attribute. For example, create a `SyncedListAttribute` and have the method you pass to the `ServiceKnownTypeAttribute` return all classes in your assembly that have your attribute on them. That way when you add a new list, all you have to do is decorate it with that attribute, and you don't have to add another `ServiceKnownTypeAttribute`.
Allon Guralnek