2010-04-08 31 views
8

Đây là tình huống của tôi. Tôi đã viết một dịch vụ WCF gọi vào một trong các cơ sở mã của nhà cung cấp của chúng tôi để thực hiện các hoạt động, chẳng hạn như Đăng nhập, Đăng xuất, vv Yêu cầu của hoạt động này là chúng ta có một chuỗi nền để nhận các sự kiện là kết quả của hành động đó. Ví dụ: Hành động đăng nhập được gửi trên luồng chính. Sau đó, một số sự kiện được nhận lại từ dịch vụ của nhà cung cấp do đăng nhập. Có thể có 1, 2 hoặc nhiều sự kiện đã nhận được. Chủ đề nền chạy trên bộ hẹn giờ, nhận các sự kiện này và kích hoạt sự kiện trong dịch vụ wcf để thông báo rằng một sự kiện mới đã đến.Dịch vụ WCF với các cuộc gọi lại đến từ chủ đề nền?

Tôi đã triển khai dịch vụ WCF ở chế độ Hai mặt và được lên kế hoạch sử dụng gọi lại để thông báo cho UI biết các sự kiện đã đến. Đây là câu hỏi của tôi: Làm cách nào để gửi các sự kiện mới từ chuỗi nền tới chuỗi đang thực hiện dịch vụ?

Ngay bây giờ, khi tôi gọi OperationContext.Current.GetCallbackChannel<IMyCallback>(), OperationContext là rỗng. Có một mô hình chuẩn để giải quyết vấn đề này không?

Tôi đang sử dụng PerSession làm SessionMode của mình trên ServiceContract.

CẬP NHẬT: Tôi nghĩ tôi muốn làm cho kịch bản chính xác của mình rõ ràng hơn bằng cách chứng minh cách tôi nhận sự kiện từ mã nhà cung cấp. Thư viện của tôi nhận được mỗi sự kiện, xác định sự kiện là gì và kích hoạt một sự kiện cho sự kiện cụ thể đó.

Tôi có một dự án khác là thư viện lớp học đặc biệt để kết nối với dịch vụ của nhà cung cấp. Tôi sẽ đăng toàn bộ thi hành dịch vụ để cung cấp cho một bức tranh rõ ràng hơn:

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

Đối tượng AgentEvent chứa callback là một trong những thuộc tính của nó, và tôi đã suy nghĩ tôi muốn thực hiện các cuộc gọi lại như thế này:

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

các AgentEvent là một đối tượng quy định tại các dịch vụ:

[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 trông như thế này:

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

Gọi lại được thực hiện trên máy khách và sử dụng EventArgs porperty của AgentEvent để điền dữ liệu trên giao diện người dùng. Làm cách nào để truyền thể hiện OperationContext.Current từ luồng chính vào luồng nền?

+0

Không có đủ thông tin trong bản cập nhật của bạn để thực sự hiểu những gì đang xảy ra. Mã này nằm ở đâu? Làm thế nào để bạn thực hiện "đăng nhập"? Có một số loại phương pháp không đồng bộ không? Điều gì đang xảy ra với 'AgentEvent' đó (nó thực sự được sử dụng ở đâu)? Và loại 'agentEvent.Callback' là gì và giá trị của nó được sử dụng như thế nào/khi nào? – Aaronaught

+0

Tôi đã thêm một ví dụ hoàn chỉnh hơn về những gì tôi đang cố gắng làm. Các sự kiện tôi nhận được bị sa thải khỏi thư viện mà tôi đang sử dụng trong dịch vụ, đó là lý do tại sao tôi phải đăng ký với họ. –

Trả lời

5

OperationContext.Current chỉ khả dụng trên chuỗi thực sự đang thực hiện thao tác. Nếu bạn muốn nó có sẵn cho một chuỗi công nhân, thì bạn cần phải thực sự chuyển tham chiếu đến kênh gọi lại đến chuỗi đó.

Vì vậy, hoạt động của bạn có thể trông mơ hồ như:

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

Đây là một cách đơn giản để làm việc đó bằng cách sử dụng ThreadPool và vô danh phương pháp biến chụp. Nếu bạn muốn làm điều đó bằng chuỗi chạy tự do, bạn phải sử dụng một số ParameterizedThreadStart thay thế và chuyển thông số callback làm thông số.


Cập nhật ví dụ cụ thể:

Có vẻ rằng những gì đang xảy ra ở đây là IVendorService sử dụng một số mô hình hướng sự kiện cho callbacks.

Vì bạn đang sử dụng InstanceContextMode.PerSession, bạn thực sự có thể lưu trữ gọi lại trong trường riêng tư của lớp dịch vụ, sau đó tham chiếu trường đó trong trình xử lý sự kiện của bạn.

[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(...); 
    } 
} 

Nếu bạn quyết định chuyển sang chế độ cá thể khác sau này thì thao tác này sẽ không hoạt động, vì mỗi khách hàng sẽ có một cuộc gọi lại khác. Miễn là bạn giữ nó ở chế độ phiên, điều này sẽ ổn thôi.

+0

@Aaronaught, tôi đã thêm mã ở trên cụ thể vào ví dụ của tôi. Bất kỳ suy nghĩ về việc thực hiện với kịch bản đó? Cụ thể, phương thức Login() của tôi không được gắn với cuộc gọi lại bằng bất kỳ cách nào. Sự kiện LoggedIn xuất hiện sau khi đăng nhập được bắt đầu, nhưng đôi khi không nhận được (trong trường hợp xảy ra lỗi giao tiếp, v.v.). Vì lý do này, tôi kích hoạt các sự kiện từ thư viện kết thúc của tôi để cho biết khi nào họ được nhận. –

+0

Hoàn hảo! Thiết lập gọi lại như là một biến địa phương đã làm các trick. Cảm ơn bạn đã giúp đỡ! –

0

Đặt các sự kiện được đề cập trong một hàng đợi an toàn chủ đề (Đã khóa) và có dịch vụ thực thi (khi bạn gọi nó) kiểm tra số lượng hàng đợi. Dequeue khi cần thiết.

0

Bạn đã không trình bày IVendorServer, nhưng chỉ trong trường hợp bạn không biết, nó đòi hỏi các thuộc tính sau:

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

Ngoài ra, bạn không cần một sợi nền để nhận các sự kiện (Tôi m thậm chí không chắc chắn nếu và làm thế nào nó có thể áp dụng). Tất cả những gì bạn cần là triển khai API gọi lại trong lớp mở rộng IMyCallback. Trong API đó là nơi nhận được thư trả lời.

Bạn cũng có thể có một bộ sưu tập của IMyCallback trường hợp của khách hàng được xác thực, cộng với một chuỗi riêng biệt để thực thi cuộc gọi lại không đồng bộ, nhưng đó là cách vượt ra ngoài phạm vi câu hỏi của bạn và là thứ đòi hỏi lập kế hoạch phương pháp.

Các vấn đề liên quan