WCF Publish / Subscribe и использование обратных вызовов для отправки данных конкретным пользователям - PullRequest
4 голосов
/ 26 мая 2010

Я сейчас работаю над проектом и немного застрял. Я создаю клиент-серверное приложение, которое позволяет клиенту подписываться на сервер, чтобы ему пересылались сообщения.

Проблема, с которой я сталкиваюсь, заключается в том, что, когда клиент подписывается, я хочу, чтобы он получал только те обновления, которые относятся к ним. Система в основном передает сообщения от БД SQL-сервера, которые контролирует сервер. При получении нового сообщения сервер должен пересылать сообщение только тем клиентам, к которым оно применяется, в зависимости от того, кто вошел на клиентский компьютер.

Я посмотрел и нашел примеры кода, которые регистрируют сообщения для рассылки всем подписавшимся клиентам, но не те, которые показывают, как идентифицировать отдельных клиентов и применяются ли к ним сообщения.

Если кто-нибудь может помочь или указать мне правильное направление, это будет оценено.

edit Просто, чтобы быть более понятным, я не столько удивляюсь, как управлять обратными вызовами и подпиской, но как управлять службой подписки, где, когда пользователь подписывается, они могут предоставить свой идентификатор пользователя вместе с информацией обратного вызова , который затем можно использовать для определения того, каким конкретным пользователям необходимо отправлять сообщения.

Теперь вы можете найти мой код ниже:

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

    }

Наконец, на данный момент это не работает, так как в основном, когда пользователь подписывается, когда он проходит через него, я хочу, чтобы он фильтровал запрос LINQ на основе идентификатора пользователя user:

#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 ]

4 голосов
/ 26 мая 2010

Есть много способов сделать это. Учитывая, что вы используете статический список для поддержки своих подписчиков, вы можете создать новый объект, например, так:

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

Затем сохраните своих подписчиков в List<Subscriber> вместо List<IMessageCallback> объекта.

Затем вы можете изменить метод Subscribe (), чтобы получить строковый параметр для имени пользователя. Это позволит вам использовать запрос linq to objects, чтобы найти пользователя, которому вы хотите отправить сообщение.

Эта техника может работать для любого идентификатора, но я не уверен, как вы пытаетесь фильтровать сообщения. Похоже, вы хотите это по имени пользователя, поэтому я использовал эту опцию здесь. Но вы также можете легко указать флаги Enum для типов подписок и передать их.

Если вам нужна альтернатива хранению ваших подписчиков в статическом списке, вы можете прочитать статью, которую я написал о регулировании WCF, в которой я использую GenericDelegates. Это может дать вам больше вариантов и идей. http://www.codeproject.com/KB/WCF/wcfesb.aspx. В этой статье также будет показан более простой способ обслуживания абонентов, чем проверка состояния контекста при каждом вызове.

2 голосов
/ 26 мая 2010

Взгляните на платформу WCF для публикации и подписки Джувала Лоуи, которая довольно подробно описана в этой статье MSDN . Код доступен для просмотра через статью, или вы можете скачать исходный код и пример с сайта Лоуи здесь . Перейдите в раздел «Загрузки», выберите фильтр по категории «Обнаружение», и вы увидите его там.

Я использую этот механизм в своем приложении WCF, и он работает как шарм. Надеюсь, это поможет.

1 голос
/ 26 мая 2010

Вы можете использовать DuplexChannel . Для этого вы должны предоставить привязку, которая поддерживает сеансовую и дуплексную связь. Затем клиенты должны будут передать InstanceContext, созданный с помощью экземпляра CallbackHandler. Наконец, сервер получит контекст (для сообщений обратного вызова), используя:

OperationContext.Current.GetCallbackChannel<ICallBackServiceContract>();

, где ICallBackServiceContract - контракт, реализованный на клиенте.

Чтобы узнать больше о дуплексных службах, см .: Дуплексные услуги

РЕДАКТИРОВАТЬ: Ну, если обратный вызов работает нормально, я имею в виду поведение экземпляра. Попробуйте добавить (или изменить) реализацию контракта, используя режим экземпляра PerSession:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...