WCF发布/订阅并使用回调将数据发送给特定用户

时间:2010-05-26 13:04:37

标签: c# wcf callback

我目前正在开展一个项目并且已经陷入困境。我正在创建一个客户端服务器应用程序,它允许客户端订阅服务器以将消息转发给它。

我遇到的问题是,当客户订阅时,我希望他们只收到与他们相关的更新。系统基本上从服务器监视的SQL服务器DB传递消息。收到新邮件后,服务器应该只根据客户端计算机上记录的内容将邮件转发给它应用的客户端。

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

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

编辑为了更清楚我不是很想知道如何操作回调和订阅但是如何操作订阅服务,当用户订阅时,他们可以提供用户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;
        }

    }

最后,当用户订阅循环时,目前这根本不起作用我希望它根据用户userID过滤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

3 个答案:

答案 0 :(得分:4)

有很多方法可以实现这一目标。考虑到您使用静态列表来维护订阅者,您可以生成一个新对象,如下所示:

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

然后将订阅者存储在List<Subscriber>而不是List<IMessageCallback>对象中。

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

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

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

您可以使用 DuplexChannel 。为此,您必须提供支持会话通信和双工通信的绑定。然后,客户端必须传递使用CallbackHandler实例构造的InstanceContext。最后,服务器将使用:

获取上下文(用于回调消息)
OperationContext.Current.GetCallbackChannel<ICallBackServiceContract>();

其中ICallBackServiceContract是客户端上实现的合同。

要了解有关双工服务的详情,请参阅:Duplex Services

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

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]