代码之家  ›  专栏  ›  技术社区  ›  Mark Struzinski

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

  •  8
  • Mark Struzinski  · 技术社区  · 16 年前

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

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

    现在,当我打电话的时候 OperationContext.Current.GetCallbackChannel<IMyCallback>() ,OperationContext为空。有没有一个标准的模式来解决这个问题?

    我正在使用Persession作为服务合同的会话模式。

    更新: 我想通过演示如何从供应商代码接收事件,我可以更清楚地说明我的具体场景。我的库接收每个事件,确定事件是什么,并为该特定事件触发一个事件。

    我有另一个项目,它是一个类库,专门用于连接到供应商服务。我将发布服务的整个实现,以提供更清晰的画面:

        [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是在服务中定义的对象:

    [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实例从主线程传递到后台线程?

    3 回复  |  直到 13 年前
        1
  •  5
  •   Aaronaught    16 年前

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

    如果您稍后决定切换到不同的实例模式,那么这将不起作用,因为每个客户机都有不同的回调。只要将其保持在会话模式下,就可以了。

        2
  •  0
  •   ΩmegaMan    16 年前

    将有问题的事件放入 线程安全(锁定) 队列并让正在执行的服务(如您所说)检查队列的计数。根据需要出列。

        3
  •  0
  •   Steve Czetty Wilko van der Veen    13 年前

    你没有出席 IVendorServer ,但如果您不知道,它需要以下属性:

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

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

    你也可以收集 imy回拨 经过身份验证的客户机实例,加上一个单独的线程来执行异步回调,但这远远超出了您的问题范围,并且需要有条理的计划。