2

我正在以发布-订阅模式编写 WCF 服务。

当有人发布事件时,我不想立即将其发送给所有客户。

对于每个客户,我希望能够检查该客户是否需要收到有关该发布的通知。

基本上,这将通过访问数据库来完成,并检查该客户端是否使用这些参数订阅了该特定事件(不能提前完成,只需要针对数据库进行检查)。

目前我正在使用这个基于列表的发布订阅者示例,但它的工作方式是 - 当一个事件被发布时 - 客户端会话被单独触发以发送消息。

所以现在,我正在改变这个:

 public void PriceChangeHandler(object sender, PriceChangeEventArgs e)
 {
     _callback.PriceChange(e.Item, e.Price, e.Change);
 }

对此:

 public void PriceChangeHandler(object sender, PriceChangeEventArgs e)
 {
     // Perform some database checks using BL if this client needs to be notified about this event

     // Only if answer is YES - call the callback function on that client
     _callback.PriceChange(e.Item, e.Price, e.Change);

     // Also - send the client an EMAIL + SMS
     _emailServer.SendEmail(e.Item);
     _smsServer.SendSMS(e.Item);
 }

两个问题:

这是正确的方法吗?我怎么知道“这个”客户是什么?客户是否应该以我将存储的“订阅”方法向我发送凭据?或者我应该实现一个自定义的“UsernameValidator”来存储 Principal 吗?

我不应该有一个所有客户的静态列表,我将发送给我的 BL,而 BL 只会返回我必须发送消息的那些客户吗?

4

2 回答 2

3

我认为首先回答这个问题会让生活变得更轻松:

我怎么知道“这个”客户是什么?

OperationContext.Current.GetCallbackChannel<T>

对于服务接收到的每个调用,都会有一个客户端通道,通过该通道进行调用,这将为您提供仅进行该调用的客户端的回调通道,这是您能够区分客户端的简单方法.

关于整个场景的方法,我将首先按照您自己的建议将列表存储subscribers在静态dictionary中,但还要保留每个客户端回调实例及其用户名:

private static Dictionary<IPriceChangeCallback, string> subscribers = new Dictionary<IPriceChangeCallback, string>();

您的回调合同在哪里IPriceChangeCallback,字符串可以是唯一的用户名或任何标识符。因此,您现在具有区分客户的基本能力,例如,假设您要将最后收到的消息发布给除发送者之外的每个客户,您将:

        lock (subscribers)
        {
            foreach (var _subscriber in subscribers)
            {
                if (OperationContext.Current.GetCallbackChannel<IPriceChangeNotification>() == _subscriber.Key)
                {
                      //if the person who sent the last message is the current subscriber, there is no need to
                      //publish the message to him, so skip this iteration
                        continue;
                }
                else
                {
                       //GetCurrrentClient is a handy method, you can optionally include this  
                       //in your callbacks just to let your clients know who exactly sent the publication
                        _subscriber.Key.PriceChangeCallback(e.Item, e.Price, e.Change, GetCurrentClient());
                }
             }
         }

或根据用户名区分您的客户,理想情况下您也应该在数据库中拥有这些用户名:

            lock (subscribers)
            {
                foreach (var _subscriber in subscribers)
                {
                    if(_subscriber.Value == "Jimmy86"))
                    {
                        //Identify a specific client by their username and don't send the notification to him
                        //here we send the notification to everyone but jimmy86
                        continue;
                    }
                    else
                    {
                        _subscriber.Key.PriceChangeCallback(e.Item, e.Price, e.Change, GetCurrentClient());
                    }
                }
            }

再说一次,每当你想知道是谁调用了服务操作,并告诉你的客户是谁发送了那个特定的消息,使用GetCurrentClient()我之前提到的方法:

    private string GetCurrentClient()
    {
        return clients[OperationContext.Current.GetCallbackChannel<IPriceChangeNotification>()];
    } 

这是正确的方法吗?

我不确定上面的方法有多可取,但我以前曾经做过,只要我想保留一个客户列表并在他们身上调用一些方法。

客户是否应该以我将存储的“订阅”方法向我发送凭据?

是的,这是一种常见的方法。对您的服务进行Subscribe()操作,这将是您的客户在想要加入您的服务时调用的第一个方法:

        [OperationContract(IsOneWay = true)]
        public void Subscribe(string username)
        {
            lock (subscribers)
            {
                subscribers.Add(OperationContext.Current.GetCallbackChannel<IPriceChangeNotification>(), username);
            }
        }

几个月前,我正在开发 Pub/Sub Silverlight 服务,我发现这篇文章及其随附的视频非常宝贵。

于 2012-06-21T22:52:48.487 回答
0

我想出的答案是实现“自定义用户名密码验证器”,因此每个服务实例现在都知道连接到它的客户端(这样我就不必在订阅中传递任何内容)。

当“发布”事件到达时 - 我会检查它的目标用户(同一用户可能从多台机器连接)。

然后,我将向目标用户提出“PriceChangeEvent”,并且将为所有客户端实例引发“PriceChangeHandler”事件。

然后,在事件内部——我会检查记录的主体是否是目标用户,如果是——我会在客户端机器上调用回调函数。

这省去了保存已连接客户端列表的麻烦,而且我不需要在“订阅”方法中传递任何内容。

于 2012-06-23T07:15:38.807 回答