2010-05-26 63 views
4

我正在開發一個項目,並且變得有點卡住了。我正在創建一個客戶端服務器應用程序,它允許客戶端訂閱服務器以將消息轉發給它。WCF發佈/訂閱並使用回調向特定用戶發送數據

我遇到的問題是,當客戶訂閱我希望他們只接收與他們有關的更新。系統基本上傳遞來自服務器監控的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; 
     } 

    } 

這最後的時刻isnt't作爲基本工作當用戶訂閱,因爲它循環通過我希望它過濾基於用戶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 

回答

4

有很多方法可以做到這一點。考慮到你正在使用一個靜態的列表,以保持你的用戶,你可能會產生像這樣一個新的對象:

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

然後存儲你的用戶在List<Subscriber>而不是List<IMessageCallback>對象。

然後,您可以修改您的Subscribe()方法爲用戶名採取字符串參數。這將允許你使用你的linq來查詢對象以找到你想要發送消息的用戶。

這種技術可以適用於任何標識符,但我不知道你是如何試圖過濾消息的。看起來你喜歡用戶名,這就是我在這裏使用這個選項的原因。但你可以很容易地有一個標誌枚舉他們類型的訂閱,並通過它。

如果你想要一個替代方案來存儲你的訂閱者在一個靜態列表中,你可以看看我寫的關於Throttling WCF的文章,使用GenericDelegates。這可能會給你更多的選擇和想法。 http://www.codeproject.com/KB/WCF/wcfesb.aspx。本文還將向您展示維護訂戶的簡單方法,而不是在每次調用時檢查上下文狀態。

+0

非常感謝您的想法,閱讀您的文章,它適合我期待這樣做,謝謝。 – manemawanna 2010-05-27 08:20:58

1

您可以請使用DuplexChannel。爲此,您必須提供支持會話通信和雙工通信的綁定。然後,客戶端必須傳遞一個由CallbackHandler實例構造的InstanceContext。最後,服務器將獲得上下文(回調消息),使用:

OperationContext.Current.GetCallbackChannel<ICallBackServiceContract>(); 

其中ICallBackServiceContract是在客戶端上執行的合同。

要了解更多關於雙工服務,請參閱:Duplex Services

編輯:好吧,如果回調是工作的罰款,我的意思是這是一個實例的行爲。嘗試添加(或修改)的合同執行,使用PerSession實例模式:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] 
+0

你好,謝謝你,我已經在使用一個回調,雙工。我需要的是說一條消息只適用於一個用戶,它只應該在用戶訂閱而不是每個人時才發送給用戶。我會更新我的原始帖子,向您展示我擁有的代碼,儘管它尚未完全完成。 – manemawanna 2010-05-26 13:38:36

2

看一看朱瓦爾·洛的發佈 - 訂閱WCF框架,這是相當不錯的詳細描述this MSDN article。該代碼可通過文章查看,或者您可以從Lowy的網站here下載源代碼和示例。轉到下載部分,按發現類別進行過濾,然後您會看到它。

我在我的WCF應用程序中使用這種機制,它像一個魅力。希望這可以幫助。