回调来自后台线程的WCF服务?

这是我的情况。 我编写了一个WCF服务,该服务调用我们供应商的代码库之一来执行操作,例如Login,Logout等。此操作的一个要求是我们有一个后台线程来接收由该操作引起的事件。 例如,Login操作在主线程上发送。 然后,由于登录,从供应商服务接收到多个事件。 可以收到1,2或几个事件。 在计时器上运行的后台线程接收这些事件并在wcf服务中触发事件以通知新事件已到达。

我已经在双工模式下实现了WCF服务,并计划使用回调来通知UI事件已到达。 这是我的问题:如何将后台线程中的新事件发送到正在执行服务的线程?

现在,当我调用OperationContext.Current.GetCallbackChannel() ,OperationContext为null。 有没有一个标准模式可以解决这个问题?

我在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(); 

AgentEvent是服务中定义的对象:

 [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属性来填充UI上的数据。 如何将OperationContext.Current实例从主线程传递到后台线程?

OperationContext.Current仅在实际执行操作的线程上可用。 如果您希望它可供工作线程使用,那么您需要实际将对该回调通道的引用传递给该线程。

所以你的操作可能看起来像模糊的东西:

 public class MyService : IMyService { public void Login() { var callback = OperationContext.Current.GetCallbackChannel(); 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(); 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(...); } } 

如果您决定稍后切换到其他实例模式,那么这将无效,因为每个客户端将具有不同的回调。 只要你保持会话模式,这应该没问题。

将有问题的事件放在线程安全(已锁定)队列中,并让执行服务(当您调用它时)检查队列的计数。 根据需要出列队列。

您没有呈现IVendorServer ,但是如果您不知道,则需要以下属性:

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

此外,您不需要后台线程来接收事件(我甚至不确定它是否以及如何适用)。 您所需要的只是在扩展IMyCallback的类中实现回调API。 在该API中是收到回复的地方。

您还可以拥有经过身份validation的客户端的IMyCallback实例的集合,以及执行异步回调的单独线程,但这超出了您的问题的范围,并且需要进行有条理的规划。