简体   繁体   中英

WCF Callback service Subscribe\Unsubscribe

I trying to understand how to work with callbacks of WCF. I was create next interfaces:

public interface INotifierCallback : IEquatable<INotifierCallback>
{
    /// <summary>
    /// Send notification.
    /// </summary>
    /// <param name="notification">Notification.</param>
    [OperationContract(IsOneWay = true)]
    void SendNotificationBack(string notification);
}

[ServiceContract(Namespace = "http://MyWCFLearning.com/NorthwindCallbackService",
    CallbackContract = typeof(INotifierCallback))]
public interface INorthwindCallbackService
{
    /// <summary>
    /// Subscribe to notifications.
    /// </summary>
    [OperationContract]
    void Subscribe();

    /// <summary>
    /// Unsubscribe from notifications.
    /// </summary>
    [OperationContract]
    void Unsubscribe();

    /// <summary>
    /// Send notification.
    /// </summary>
    /// <param name="notification">Notification.</param>
    [OperationContract(IsOneWay = true)]
    void SendNotification(string notification);
}

I implement this interface by the next way:

public class NorthwindCallbackService : INorthwindCallbackService
    {
        /// <summary>
        /// Callbacks to clients.
        /// </summary>
        protected static IDictionary<INotifierCallback, byte> mCallbacks { get; set; }

        static NorthwindCallbackService()
        {
            NorthwindCallbackService.mCallbacks = new ConcurrentDictionary<INotifierCallback, byte>();
        }

        public void Subscribe()
        {
            INotifierCallback callbackForClient;

            callbackForClient = OperationContext.Current.GetCallbackChannel<INotifierCallback>();
            if (NorthwindCallbackService.mCallbacks.ContainsKey(callbackForClient) == false)
            {
                NorthwindCallbackService.mCallbacks.Add(callbackForClient, default(byte));
            }
        }

        public void Unsubscribe()
        {
            INotifierCallback callbackForClient;

            callbackForClient = OperationContext.Current.GetCallbackChannel<INotifierCallback>();
            if (NorthwindCallbackService.mCallbacks.ContainsKey(callbackForClient))
            {
                NorthwindCallbackService.mCallbacks.Remove(callbackForClient);
            }
        }

        public void SendNotification(string notification)
        {
            foreach (var currentCallback in NorthwindCallbackService.mCallbacks)
            {
                try
                {
                    currentCallback.Key.SendNotificationBack(notification);
                }
                catch (ObjectDisposedException)
                {
                    //TODO: When client of NorthwindCallbackService call Dispose() method, we should remove callback of him from NorthwindCallbackService.mCallbacks, but I do not know how to make it.
                }
            }
        }
    }

Then I create UnitTest project where I added next method for test:

[TestMethod]
public void SubscribeAndUnsubscribeTest()
{
    INorthwindCallbackServiceCallback callbackHandler;
    InstanceContext instanceContext;

    callbackHandler = new FakeINorthwindCallbackServiceCallback();
    instanceContext = new InstanceContext(callbackHandler);

    using (var callbackServiceClient = new NorthwindCallbackServiceClient(instanceContext))
    {
        callbackServiceClient.Subscribe();
        callbackServiceClient.Unsubscribe();
    }
}

And I have NotSupportedException for Equals method when execute next line of WCF service:

if (NorthwindCallbackService.mCallbacks.ContainsKey(callbackForClient))

I understand that reason of this problem is not implemented IEquatable, but how to implement it on server side, and what is the best way to use callbacks?

This answer is about the last part of the question:

what is the best way to use callbacks?

You got the idea covered, but why are you not using the byte value of the ConcurrentDictionary ? First of all, there is no 'best way'. But I propose to change the ConcurrentDictionary in such a way that you can store a client id together with the callback info into the ConcurrentDictionary so you can query easier and you can store more info about the client:

public class NorthwindCallbackService : INorthwindCallbackService
{
    /// <summary>
    /// Callbacks to clients.
    /// </summary>
    protected static IDictionary<Guid, INotifierCallback> mCallbacks { get; set; }

    static NorthwindCallbackService()
    {
        NorthwindCallbackService.mCallbacks = new ConcurrentDictionary<Guid, INotifierCallback>();
    }

    public void Subscribe(Guid clientId)
    {
        INotifierCallback callbackForClient;

        callbackForClient = OperationContext.Current.GetCallbackChannel<INotifierCallback>();
        if (NorthwindCallbackService.mCallbacks.ContainsKey(clientId) == false)
        {
            NorthwindCallbackService.mCallbacks.Add(clientId, callbackForClient);
        }
    }

    public void Unsubscribe(Guid clientId)
    {
        INotifierCallback callbackForClient;

        callbackForClient = OperationContext.Current.GetCallbackChannel<INotifierCallback>();
        if (NorthwindCallbackService.mCallbacks.ContainsKey(clientId))
        {
            NorthwindCallbackService.mCallbacks.Remove(clientId);
        }
    }

    public void SendNotification(string notification)
    {
        foreach (var currentCallback in NorthwindCallbackService.mCallbacks)
        {
            try
            {
                currentCallback.Value.SendNotificationBack(notification);
            }
            catch (ObjectDisposedException)
            {
                //TODO: When client of NorthwindCallbackService call Dispose() method, we should remove callback of him from NorthwindCallbackService.mCallbacks, but I do not know how to make it.
            }
        }
    }
}

Now since you can identify the clients you can also send notifications to specific clients if you extend the SendNotification method with a Guid parameter.

You could even replace the ConcurrentDictionary<Guid, INotifierCallback> with an instance of a custom class that contains more information about the client like Id, type of notifications it likes to receive, ip-address or whatever you want using a ConcurrentDictionary<Guid, ClientCallbackInfo> construct:

public class ClientCallbackInfo
{
    public INotifierCallback Callback { get; set; }
    public string SomeOtherClientInfo { get; set; } 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM