1

我试图了解如何使用 WCF 的回调。我正在创建下一个接口:

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);
}

我通过下一种方式实现这个接口:

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.
                }
            }
        }
    }

然后我创建了 UnitTest 项目,在其中添加了下一个测试方法:

[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();
    }
}

在执行 WCF 服务的下一行时,我对 Equals 方法有 NotSupportedException:

if (NorthwindCallbackService.mCallbacks.ContainsKey(callbackForClient))

我知道这个问题的原因不是实现了 IEquatable,而是如何在服务器端实现它,以及使用回调的最佳方法是什么?

4

1 回答 1

2

这个答案是关于问题的最后一部分:

使用回调的最佳方法是什么?

您已经了解了这个想法,但是为什么不使用 的byteConcurrentDictionary?首先,没有“最好的方法”。但我建议以ConcurrentDictionary这样一种方式进行更改,即您可以将客户端 ID 与回调信息一起存储到 中,ConcurrentDictionary这样您就可以更轻松地查询,并且可以存储有关客户端的更多信息:

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.
            }
        }
    }
}

现在,由于您可以识别客户端,如果您SendNotification使用参数扩展该方法,您还可以向特定客户端发送通知Guid

您甚至可以将 替换ConcurrentDictionary<Guid, INotifierCallback>为自定义类的实例,该类包含有关客户端的更多信息,例如 Id、它喜欢接收的通知类型、IP 地址或您想要使用ConcurrentDictionary<Guid, ClientCallbackInfo>构造的任何内容:

public class ClientCallbackInfo
{
    public INotifierCallback Callback { get; set; }
    public string SomeOtherClientInfo { get; set; } 
}
于 2017-01-22T11:43:14.553 回答