2010-04-08 47 views
8

這是我的情況。我寫了一個WCF服務,它調用我們供應商的代碼庫來執行操作,例如登錄,註銷等。此操作的要求是我們有一個後臺線程來接收由此操作導致的事件。例如,登錄操作在主線程上發送。然後,作爲登錄的結果,從供應商服務接收到幾個事件。可以收到1個,2個或幾個事件。後臺線程運行在一個定時器上,接收這些事件並在wcf服務中觸發一個事件來通知新事件已經到達。帶有來自後臺線程的回調的WCF服務?

我已經在雙工模式下實現了WCF服務,並且計劃使用回調來通知UI事件已經到達。這是我的問題:我如何從後臺線程發送新事件到正在執行服務的線程?

現在,當我撥打OperationContext.Current.GetCallbackChannel<IMyCallback>()時,OperationContext爲空。有沒有一個標準模式來解決這個問題?

我在ServiceContract上使用PerSession作爲我的SessionMode。

更新: 我想我會通過演示如何從供應商代碼接收事件來使我的確切場景更加清晰。我的圖書館收到每個事件,確定事件是什麼,併爲特定事件觸發事件。

我有另一個專門用於連接到供應商服務的類庫。我會後服務的整個實施給予更清晰的畫面:

[ServiceBehavior(
     InstanceContextMode = InstanceContextMode.PerSession 
     )] 
    public class VendorServer:IVendorServer 
    { 
private IVendorService _vendorService; // This is the reference to my class library 

     public VendorServer() 
     { 
_vendorServer = new VendorServer(); 
_vendorServer.AgentManager.AgentLoggedIn += AgentManager_AgentLoggedIn; // This is the eventhandler for the event which arrives from a background thread 

} 

     public void Login(string userName, string password, string stationId) 
     { 
      _vendorService.Login(userName, password, stationId); // This is a direct call from the main thread to the vendor service to log in 
     } 

    private void AgentManager_AgentLoggedIn(object sender, EventArgs e) 
    { 

     var agentEvent = new AgentEvent 
          { 
           AgentEventType = AgentEventType.Login, 
           EventArgs = e 
          }; 
    } 
} 

的AgentEvent對象包含回調作爲它的一個屬性,我想我會執行回調是這樣的:

agentEvent.Callback = OperationContext.Current.GetCallbackChannel<ICallback>(); 

的AgentEvent是在服務中定義的對象:

0123:

[DataContract] 
public class AgentEvent 
{ 
    [DataMember] 
    public EventArgs EventArgs { get; set; } 
    [DataMember] 
    public AgentEventType AgentEventType { get; set; } 
    [DataMember] 
    public DateTime TimeStamp{ get; set; } 
    [DataMember] 
    public IVendorCallback Callback { get; set; } 
} 

IVendorCallback看起來像這樣

public interface IVendorCallback 
    { 
     [OperationContract(IsOneWay = true)] 
     void SendEvent(AgentEvent agentEvent); 
    } 

該回調在客戶端上實現,並使用AgentEvent的EventArgs porperty在UI上填充數據。 如何將主線程的OperationContext.Current實例傳遞給後臺線程?

+0

您的更新中沒有足夠的信息來真正理解正在發生的事情。此代碼位於何處?你如何執行「登錄」?它是某種異步方法嗎?那個'AgentEvent'發生了什麼?(它實際使用在哪裏)?那麼'agentEvent.Callback'的類型是什麼以及它的值如何/何時被使用? – Aaronaught 2010-04-09 13:33:54

+0

我添加了一個更完整的例子,我正在嘗試做什麼。我收到的事件是從我在服務中使用的圖書館中發出的,這就是爲什麼我必須訂閱它們。 – 2010-04-09 13:50:51

回答

5

OperationContext.Current僅在實際執行操作的線程上可用。如果你希望工作線程可以使用它,那麼你需要實際上將一個引用通過回調通道傳遞給該線程。

所以你的操作可能看起來像含糊:

public class MyService : IMyService 
{ 
    public void Login() 
    { 
     var callback = 
      OperationContext.Current.GetCallbackChannel<ILoginCallback>(); 
     ThreadPool.QueueUserWorkItem(s => 
     { 
      var status = VendorLibrary.PerformLogin(); 
      callback.ReportLoginStatus(status); 
     }); 
    } 
} 

這是使用ThreadPool和匿名方法捕獲變量做的一個簡單方法。如果您想使用自由運行的線程執行此操作,則必須改用ParameterizedThreadStart,並將callback作爲參數。


更新具體的例子:

看來,這是怎麼回事就在於IVendorService使用回調一些事件驅動模型。

由於您使用InstanceContextMode.PerSession,可以真正回調存儲在服務類本身的私人領域,然後引用在事件處理的那場。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] 
public class VendorServer : IVendorServer 
{ 
    private IMyCallback callback; 
    private IVendorService vendorService; 

    public VendorServer() 
    { 
     callback = OperationContext.Current.GetCallbackChannel<IMyCallback>(); 
     vendorService = new VendorService(); 
     vendorService.AgentManager.AgentLoggedIn += AgentManager_AgentLoggedIn; 
    } 

    public void Login(string userName, string password, string stationId) 
    { 
     vendorService.Login(userName, password, stationId); 
    } 

    private void AgentManager_AgentLoggedIn(object sender, EventArgs e) 
    { 
     callback.ReportLoggedIn(...); 
    } 
} 

如果您決定切換到一個不同的實例模式以後,那麼這將無法工作,因爲每個客戶都會有不同的回調。只要你保持會話模式,這應該沒問題。

+0

@Aaronaught,我添加了特定於我的實例的代碼。有關實施該方案的任何想法? 具體來說,我登錄()方法是不依賴於任何方式回調。 LoggedIn事件發生在登錄啓動後,但有時不會被接收(如果發生通信故障等)。出於這個原因,我從我的後端庫中發出事件以指示它們何時收到。 – 2010-04-09 12:57:15

+0

完美!將回調設置爲局部變量有訣竅。謝謝你的幫助! – 2010-04-09 15:38:15

0

將相關事件置於線程安全(鎖定)隊列中,並讓執行中的服務(如您調用它)檢查隊列的計數。根據需要出列。

0

您沒有提出IVendorServer,但以防萬一你不知道,它需要以下屬性:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IMyCallback))] 

而且,你並不需要一個後臺線程接收事件(I」米甚至不知道是否以及如何適用)。 所有你需要的是落實在擴展IMyCallback類回調API。 在該API中收到答覆。

您也可以擁有已驗證客戶端的IMyCallback實例集合,以及執行異步回調的單獨線程,但這超出了您的問題範圍,並且需要進行有條不紊的規劃。