【问题标题】:WCF Publish/Subscribe and using callbacks to send data to specific usersWCF 发布/订阅并使用回调将数据发送给特定用户
【发布时间】:2010-05-26 13:04:37
【问题描述】:

我目前正在做一个项目,但有点卡住了。我正在创建一个客户端服务器应用程序,它允许客户端订阅服务器以将消息转发给它。

我遇到的问题是,当客户订阅时,我希望他们只收到与他们相关的更新。系统基本上从服务器监控的 SQL 服务器 DB 传递消息。当收到一条新消息时,服务器应该只根据谁在客户端机器上登录,将该消息转发给它所应用的客户端。

我查看并找到了代码示例,这些示例注册了要在所有已订阅的客户之间广播的消息,但没有显示如何识别单个客户以及消息是否适用于他们的代码示例。

如果有人可以帮助或指出正确的方向,我将不胜感激。

edit 为了更清楚,我不是很想知道如何操作回调和订阅,而是如何操作订阅服务,当用户订阅时,他们可以在回调中提供用户 ID信息,然后可用于识别需要向哪些特定用户发送消息。

您现在可以在下面找到我的一些代码:

namespace AnnouncementServiceLibrary
{
    [ServiceContract(CallbackContract = typeof(IMessageCallback))]
    public interface IMessageCheck
    {
        [OperationContract]
        void MessageCheck();
    }
}

namespace AnnouncementServiceLibrary
{
    public interface IMessageCallback
    {
        [OperationContract(IsOneWay = true)]
        void OnNewMessage(Mess message);
    }
}

订阅/取消订阅:

private static readonly List<IMessageCallback> subscribers = new List<IMessageCallback>();

        public bool Subscribe()
    {
        try
        {

            IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();

            //If they dont already exist in the subscribers list, adds them to it
            if (!subscribers.Contains(callback))
                subscribers.Add(callback);
            return true;
        }
        catch
        {
            //Otherwise if an error occurs returns false
            return false;
        }
    }


    /// <summary>
    /// Unsubscribes the user from recieving new messages when they become avaliable
    /// </summary>
    /// <returns>Returns a bool that indicates whether the operation worked or not</returns>
    public bool Unsubscribe()
    {
        try
        {

            IMessageCallback callback = OperationContext.Current.GetCallbackChannel<IMessageCallback>();

            //If they exist in the list of subscribers they are then removed
            if (subscribers.Contains(callback))
                subscribers.Remove(callback);
            return true;
        }
        catch
        {
            //Otherwise if an error occurs returns false
            return false;
        }

    }

最后,当用户订阅时,它基本上不能正常工作,因为它循环通过我希望它根据用户的用户 ID 过滤 LINQ 查询:

#region IMessageCheck Members

        /// <summary>
        /// This method checks for new messages recieved based on those who have subscribed for the service
        /// </summary>
        public void MessageCheck()
        {
            //A continuous loop to keep the method going
            while(true)
            {
                //Changes the thread to a sleep state for 2 mins?
                Thread.Sleep(200000);

                //Go through each subscriber based on there callback information
                subscribers.ForEach(delegate(IMessageCallback callback)
                {
                    //Checks if the person who wanted the callback can still be communicated with
                    if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                    {
                        //Creates a link to the database and gets the required information
                        List<Mess> mess = new List<Mess>();
                        List<Message> me;
                        List<MessageLink> messLink;

                        AnnouncementDBDataContext aDb = new AnnouncementDBDataContext();

                        me = aDb.Messages.ToList();
                        messLink = aDb.MessageLinks.ToList();

                        //Query to retrieve any messages which are newer than the time when the last cycle finished
                        var result = (from a in messLink
                                      join b in me
                                          on a.UniqueID equals b.UniqueID
                                      where b.TimeRecieved > _time
                                      select new { b.UniqueID, b.Author, b.Title, b.Body, b.Priority, a.Read, b.TimeRecieved });

                        //Foreach result a new message is created and returned to the PC that subscribed
                        foreach (var a in result)
                        {
                            Mess message = new Mess(a.UniqueID, a.Author, a.Title, a.Body, a.Priority, (bool)a.Read, a.TimeRecieved);
                            callback.OnNewMessage(message);
                        }
                    }
                    //If the requesting PC can't be contacted they are removed from the subscribers list
                    else
                    {
                        subscribers.Remove(callback);
                    }
                });

                //Sets the datetime so the next cycle can measure against to see if new messages have been recieved
                _time = DateTime.Now;
            }

        }
        #endregion

【问题讨论】:

    标签: c# wcf callback


    【解决方案1】:

    有很多方法可以做到这一点。考虑到您使用静态列表来维护您的订阅者,您可以像这样生成一个新对象:

    class Subscriber
    {
        public string UserName { get; set; }
        public IMessageCallback CallBack { get; set; }
    }
    

    然后将您的订阅者存储在 List&lt;Subscriber&gt; 而不是 List&lt;IMessageCallback&gt; 对象中。

    然后您可以修改您的 Subscribe() 方法以获取用户名的字符串参数。这将允许您使用 linq to objects 查询来查找要向其发送消息的用户。

    此技术适用于任何标识符,但我不确定您是如何尝试过滤消息的。看起来你想要它的用户名,这就是我在这里使用这个选项的原因。但是你可以很容易地为它们的订阅类型设置一个标志枚举并将其传递进去。

    如果您想要将订阅者存储在静态列表中的替代方法,您可以查看我写的关于限制 WCF 的文章,我使用 GenericDelegates。这可能会给你更多的选择和想法。 http://www.codeproject.com/KB/WCF/wcfesb.aspx 。本文还将向您展示一种维护订阅者的方法,而不是在每次调用时检查上下文状态。

    【讨论】:

    • 非常感谢您的想法,阅读您的文章,它适合我的目标,谢谢。
    【解决方案2】:

    看看 Juval Lowy 的 Publish-Subscribe WCF 框架,在 this MSDN article 中有相当详细的描述。代码可以通过文章查看,或者您可以从 Lowy 的网站here 下载源代码和示例。转到“下载”部分,按“发现”类别进行过滤,您会在那里看到它。

    我在我的 WCF 应用程序中使用了这种机制,它就像一个魅力。希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      您可以使用 DuplexChannel。为此,您必须提供支持会话通信和双工通信的绑定。然后,客户端必须传递一个用 CallbackHandler 实例构造的 InstanceContext。最后,服务器将获取上下文(用于回调消息),使用:

      OperationContext.Current.GetCallbackChannel<ICallBackServiceContract>();
      

      其中 ICallBackServiceContract 是在客户端实现的合约。

      要了解有关双工服务的更多信息,请参阅:Duplex Services

      编辑:好吧,如果回调工作正常,我的意思是它是一个实例行为。尝试使用 PerSession 实例模式添加(或更改)合约实现:

      [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
      

      【讨论】:

      • 您好,谢谢您,我已经在使用双工回调。我需要的是说一条消息只适用于一个用户,它应该只在他们订阅后发送给那个用户,而不是每个人。我将更新我的原始帖子,向您展示我拥有的代码,虽然它还没有完全完成。
      猜你喜欢
      • 2015-11-11
      • 2011-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多