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

Mar*_*ski 8 c# wcf multithreading

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

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

现在,当我调用时OperationContext.Current.GetCallbackChannel<IMyCallback>(),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
                             };
    }
}
Run Code Online (Sandbox Code Playgroud)

AgentEvent对象包含回调作为其属性之一,我以为我会像这样执行回调:

agentEvent.Callback = OperationContext.Current.GetCallbackChannel<ICallback>();
Run Code Online (Sandbox Code Playgroud)

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; }
}
Run Code Online (Sandbox Code Playgroud)

IVendorCallback看起来像这样:

    public interface IVendorCallback
    {
        [OperationContract(IsOneWay = true)]
        void SendEvent(AgentEvent agentEvent);
    }
Run Code Online (Sandbox Code Playgroud)

回调在客户端上实现,并使用AgentEvent的EventArgs属性在UI上填充数据.如何将OperationContext.Current实例从主线程传递到后台线程?

Aar*_*ght 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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

这是使用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(...);
    }
}
Run Code Online (Sandbox Code Playgroud)

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