WCF - client callback recommended?

2019-07-18 14:07发布

问题:

I have one WCF service that exposes two operations - IncrementList() and GetList(). Client B connects to the service, calls GetList() and displays to the user. Client A has the ability to update this list by calling IncrementList().
I want Client B to get notified when IncrementList() is called so it can call GetList() again to display the updated data.

Could you please outline how you'd implement this? CallBacks? Duplex? Publisher/Subscriber?
Anything new in WCF 4.0 that aids in this scenario?

Thanks!

回答1:

Something you should decide up front is if you want to push the whole list when its incremented (heavy) or just the updated item to each client. The code below is for push whole list but easy to modify this just to push the updated object (recomended).
At app start Client B should call Register_client and then GetList. Subsequently it will be notified via call back when list is incremented (the client needs to implient this interface)
The call GetList requires a duplex channel and SessionMode.Required.

Your server should impliment:

[ServiceContract(SessionMode = SessionMode.Required
                 CallbackContract = typeof(IMyCallback))]
    public interface IMyServer {

    [OperationContract]
    void Register_client();

    [OperationContract(IsOneWay = true)]
    void IncrementList();

    [OperationContract]
    ListObject[] GetList();
}  

[DataContract]
public class ListObject {
    [DataMember]...
}

Your client should impliment:

public interface IMyCallback {
    [OperationContract(IsOneWay = true)]
    void PushList(ListObject[] list);
}

Register client just needs store client callback interface for use when list is incremented something like:

public override void Register_client() {
    // Store callback interfaces for all connected clients:
    IMyCallback callback = OperationContext.Current.GetCallbackChannel<IGatewayServerCallback>();
    if (clients.Contains(callback) == false)
    clients.Add(callback);
    Trace.WriteLine(string.Format("Client connection established ({0})", clients.Count));
}

Where:

private List<IMyCallback> clients = new List<IMyCallback>();

The impliementation of IncrementList should do a callback to push new list (or better just the new object that was added to list) to client - something like:

for (int i = 0; i < clients.Count; i++) {
    if (((ICommunicationObject)clients[i]).State == CommunicationState.Opened) {
    try {
        clients[i].PushList(list);
    }
    catch (Exception e) {
        clients.RemoveAt(i--);
        Trace.WriteLine(e);
        Trace.WriteLine(string.Format("Removing client {0} (exception).", i + 1));
    }
}

The callback implimentation (client side) looks something like:

public class MyCallback : IMyCallback {
public void PushList(ListObject[] list) {
    // Were client side - update list code here...
}

Probably this callback implimentation needs a reference to some object that holds the list data - probably this is passed in to contructor (not shown).

When you instantiate your proxy object you will need to pass an instance of callback to the proxy constructor - something like:

MyServerClient client_proxy = new MyServerClient(new InstanceContext(my_callback, binding_str)