简体   繁体   English

WCF Windows服务中当前OperationContext为null

[英]current OperationContext is null in WCF Windows Service

I am trying to set up a Publish/Subscribe system using WCF and where the WCF server is in a Windows service. 我正在尝试使用WCF以及WCF服务器在Windows服务中的位置来设置发布/订阅系统。 The binding is net.TCP. 绑定是net.TCP。 The service is providing a "Subscribe" method to the client so the client can register a callback handler to an event that will be raised from a DLL linked to the server. 该服务为客户端提供了一种“订阅”方法,因此客户端可以将回调处理程序注册到将从链接到服务器的DLL引发的事件中。 In the Subscribe method I attempt to get the callback channel using the OperationContext.Current.GetCallbackChannel method. 在Subscribe方法中,我尝试使用OperationContext.Current.GetCallbackChannel方法获取回调通道。 When I attempt this the OperationContext.Current property returns NULL. 当我尝试执行此操作时,OperationContext.Current属性返回NULL。

Can anyone tell me under what circumstances this property would return null?? 谁能告诉我该属性在什么情况下将返回null? Have I missed setting something up? 我错过了设置什么吗? I will include the service code and the interface code below. 我将在下面包括服务代码和接口代码。 I am using c# in Visual Studio 2012 and targeting framework 4.5. 我在Visual Studio 2012和定位框架4.5中使用c#。

Service: 服务:

namespace WService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class WcfPublisherService : IWcfPublisherContract
{
    IOALogic logic = new OAControlExample();
    IWcfSubscriberContract _callback = null;

    public void Subscribe()
    {
        _callback = OperationContext.Current.GetCallbackChannel<IWcfSubscriberContract>();
        logic.BarriersChanged += logic_BarriersChanged;
    }

    public void UnSubscribe()
    {
        logic.BarriersChanged -= logic_BarriersChanged;
    }

    void logic_BarriersChanged(object sender, BarriersChangedEventArgs e)
    {
        _callback.BarriersChanged(e.BarrierLines);
    }
}
}

Interface: 接口:

namespace WService
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IWcfSubscriberContract))]
public interface IWcfPublisherContract
{
    [OperationContract(IsOneWay=false, IsInitiating=true)]
    void Subscribe();
    [OperationContract(IsOneWay = false, IsTerminating=true)]
    void UnSubscribe();
}

public interface IWcfSubscriberContract
{
    [OperationContract(IsOneWay = true)]
    void BarriersChanged(BarrierLines barrierLines);
}
}

Client: 客户:

namespace TestClient
{
public partial class Form1 : Form
{
    WcfPublisherService myService
        = new WcfPublisherService();

    ServiceCallback serviceCallback = new ServiceCallback();

    public Form1()
    {
        InitializeComponent();
        serviceCallback.NewMessage += serviceCallback_NewMessage;
    }

    private delegate void serviceCallback_NewMessageDelegate(object sender, NewMessageEventArgs e);
    void serviceCallback_NewMessage(object sender, NewMessageEventArgs e)
    {
        if (textBox1.InvokeRequired)
        {
            textBox1.Invoke(new serviceCallback_NewMessageDelegate(serviceCallback_NewMessage), new object[] {sender, e});
        }
        else
        {
            if (textBox1.Text.Trim().Length > 1)
            {
                textBox1.Text += Environment.NewLine;
            }
            textBox1.Text += e.Msg;
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        myService.Subscribe();
    }

    private void button2_Click(object sender, EventArgs e)
    {
        myService.UnSubscribe();
    }
}

[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
class ServiceCallback : IWcfSubscriberContract
{
    public delegate void NewMessageEventHandler(object sender, NewMessageEventArgs e);
    public event NewMessageEventHandler NewMessage;
    protected virtual void OnNewMessage(string msg)
    {
        if (NewMessage != null)
        {
            NewMessage(this, new NewMessageEventArgs(msg));
        }
    }

    public void BarriersChanged(OA.BarrierLines barrierLines)
    {
        OnNewMessage("new barrier lines");
    }
}

public class NewMessageEventArgs : EventArgs
{
    public NewMessageEventArgs(string msg)
    {
        this.Msg = msg;
    }
    public string Msg { get; set; }
}
}

** * ** * ** * New Edit * ** * ** * ** * ** * ** Thanks to SalientBrain's suggestions, I have made considerable changes to my project because I realized the service had to be long running and continually running even if no clients are connected so I changed it to a singleton. ** * ** * ** *新编辑* ** * ** * ** * ** * **由于SalientBrain的建议,我对项目进行了相当大的更改,因为我意识到该服务必须长期运行并且即使没有客户端连接也可以持续运行,所以我将其更改为单例。 Even so, my original problem still persists. 即使这样,我原来的问题仍然存在。 SalientBrain has asked to see my config file so I will include it below along with all the other pertinent files. SalientBrain已要求查看我的配置文件,因此我将在下面将其与所有其他相关文件一起包括在内。 I've stripped it out to conserve space, but I don't think I removed anything important. 我将其删除以节省空间,但是我不认为我删除了任何重要的内容。 The error occurs in Subscribe method of the PulisherService class. 错误在PulisherService类的Subscribe方法中发生。 I hope it is something stupid I did in the config file. 我希望这是我在配置文件中所做的愚蠢的事情。 Well, here it is: 好吧,这里是:

Config: 配置:

    <configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="WService.WCFPublisherServiceBehavior">
                    <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="WService.WCFPublisherServiceBehavior"
                name="WService.WcfPublisherService">
                <endpoint address="" binding="netTcpBinding" bindingConfiguration=""
                    name="NetTcpBindingEndpoint" contract="WService.IWcfPublisherContract">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
                    name="MexTcpBindingEndpoint" contract="IMetadataExchange" />
                <host>
                    <baseAddresses>
                        <add baseAddress="net.tcp://localhost:8523/Publisher" />
                    </baseAddresses>
                </host>
            </service>
        </services>
    </system.serviceModel>
</configuration>

WcfContracts: Wcf合约:

    namespace WService
{
    [ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(IWcfSubscriberContract))]
    public interface IWcfPublisherContract
    {
        [OperationContract(IsOneWay=false)]
        void Subscribe(string key);
        [OperationContract(IsOneWay = false)]
        void UnSubscribe(string key);
    }

    public interface IWcfSubscriberContract
    {
        [OperationContract(IsOneWay = true)]
        void BarriersChanged(BarrierLines barrierLines);
    }
}

WcfService: WcfService:

    namespace WService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class WcfPublisherService : IWcfPublisherContract
    {
        private static WcfPublisherService _instance = null;
        private IOALogic _logic = null;
        private Dictionary<string, IWcfSubscriberContract> _callbacks
            = new Dictionary<string, IWcfSubscriberContract>();
        private ReaderWriterLock _callbacksLock = new ReaderWriterLock();

        private WcfPublisherService() { }

        public static WcfPublisherService TheInstance()
        {
            if (_instance == null)
            {
                _instance = new WcfPublisherService();
            }
            return _instance;
        }

        public void StopWcf()
        {
            _logic.StopRequest();
        }

        public void StartWcf(IOALogic logic)
        {
            _logic = logic;
            _logic.BarriersChanged += logic_BarriersChanged;
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartWork), null);
        }

        public void StartWork(object state)
        {
            _logic.Run();
        }

        public void Subscribe(string key)
        {
            OperationContext context = OperationContext.Current;
            // The above line returns null ***********************************************
            _callbacksLock.AcquireWriterLock(2000);
            if (_callbacksLock.IsWriterLockHeld)
            {
                _callbacks.Add(key, context.GetCallbackChannel<IWcfSubscriberContract>());
                // The above line throws a null execption because context is null  ********
                _callbacksLock.ReleaseWriterLock();
            }
        }

        public void UnSubscribe(string key)
        {
            _callbacksLock.AcquireWriterLock(2000);
            if (_callbacksLock.IsWriterLockHeld)
            {
                _callbacks.Remove(key);
                _callbacksLock.ReleaseWriterLock();
            }
        }

        void logic_BarriersChanged(object sender, BarriersChangedEventArgs e)
        {
            _callbacksLock.AcquireReaderLock(1000);
            if (_callbacksLock.IsReaderLockHeld)
            {
                try
                {
                    foreach (IWcfSubscriberContract callback in _callbacks.Values)
                    {
                        callback.BarriersChanged(e.BarrierLines);
                    }
                }
                finally
                {
                    _callbacksLock.ReleaseReaderLock();
                }
            }
        }
    }
}

WindowsService: WindowsService:

    namespace WService
{
    public partial class WService : ServiceBase
    {
        internal static ServiceHost _serviceHost = null;
        internal static IOALogic _logic = new OAControlExample();

        public WService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            if (_serviceHost != null)
            {
                _serviceHost.Close();
            }

            _serviceHost = new ServiceHost(WcfPublisherService.TheInstance());
            WcfPublisherService.TheInstance().StartWcf(_logic);
            _serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (WcfPublisherService.TheInstance() != null)
            {
                WcfPublisherService.TheInstance().StopWcf();
            }
            if (_serviceHost != null)
            {
                _serviceHost.Close();
                _serviceHost = null;
            }
        }
    }
}

TestForm: 测试表格:

    namespace TestClient
{
    public partial class Form1 : Form
    {
        ServiceCallback serviceCallback = new ServiceCallback();

        public Form1()
        {
            InitializeComponent();

            serviceCallback.NewMessage += serviceCallback_NewMessage;
        }

        private delegate void serviceCallback_NewMessageDelegate(object sender, NewMessageEventArgs e);
        void serviceCallback_NewMessage(object sender, NewMessageEventArgs e)
        {
            if (textBox1.InvokeRequired)
            {
                textBox1.Invoke(new serviceCallback_NewMessageDelegate(serviceCallback_NewMessage), new object[] {sender, e});
            }
            else
            {
                if (textBox1.Text.Trim().Length > 1)
                {
                    textBox1.Text += Environment.NewLine;
                }
                textBox1.Text += e.Msg;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            serviceCallback.Subscribe();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            serviceCallback.Unsubscribe();
        }
    }
}

TestCallbackClass: TestCallbackClass:

    namespace TestClient
{
    [CallbackBehaviorAttribute(UseSynchronizationContext = true)]
    class ServiceCallback : IWcfSubscriberContract
    {
        WcfPublisherService myService
            = WcfPublisherService.TheInstance();
        string callbackKey = Guid.NewGuid().ToString();

        public delegate void NewMessageEventHandler(object sender, NewMessageEventArgs e);
        public event NewMessageEventHandler NewMessage;
        protected virtual void OnNewMessage(string msg)
        {
            if (NewMessage != null)
            {
                NewMessage(this, new NewMessageEventArgs(msg));
            }
        }

        public void Subscribe()
        {
            try
            {
                myService.Subscribe(callbackKey);
            }
            catch (Exception ex)
            {
                OnNewMessage("exception: " + ex.Message);
            }
        }

        public void Unsubscribe()
        {
            try
            {
                myService.UnSubscribe(callbackKey);
            }
            catch (Exception ex)
            {
                OnNewMessage("exception: " + ex.Message);
            }
        }

        public void BarriersChanged(OAInterface.BarrierLines barrierLines)
        {
            OnNewMessage("new barrier lines");
        }
    }

    public class NewMessageEventArgs : EventArgs
    {
        public NewMessageEventArgs(string msg)
        {
            this.Msg = msg;
        }
        public string Msg { get; set; }
    }
}

As discussed in the comments, if you directly create an instance of the service type - as opposed to a WCF proxy/clientchannel - and then you call a method on it, there is no OperationContext. 如注释中所述,如果您直接创建服务类型的实例(与WCF代理/客户端通道相对),然后对其调用方法,则没有OperationContext。 WCF provides an OperationContext instance when your operation is running within a service. 当您的操作在服务中运行时,WCF提供一个OperationContext实例。

In the client code neither proxy created not channel factory. 在客户端代码中,既没有创建代理,也没有创建通道工厂。 Service class instance is created as a class library. 服务类实例被创建为类库。

You should consume service as below code 您应该按照以下代码使用服务

 ServiceCallback serviceCallback = new ServiceCallback();
 InstanceContext instanceContext = new InstanceContext(serviceCallback);

 var pubsubProxy = new PubSubProxy.WcfPublisherContractClient(instanceContext);
 pubsubProxy.Subscribe();

And when the service is running, OperationContext is created and you can access OperationContext.Current 并且在服务运行时,会创建OperationContext,并且您可以访问OperationContext.Current

I had a similar issue: in my case when the InstanceContextMode was set to Single the WebOperationContext.Current was null in the constructor. 我有一个类似的问题:在我的情况下,当InstanceContextMode设置为SingleWebOperationContext.Current在构造函数中为null However it was available within the services/classes methods. 但是,它在services / classs方法中可用。

In my case it was me being stupid... 就我而言,这是我的愚蠢……

I tried to set 我试图设定

 callback = OperationContext.Current.GetCallbackChannel<IWcfSubscriberContract>();

In the CALLBACK function, instead of the server side funciton... When in callback function - obviously there's no current context. 在CALLBACK函数中,而不是服务器端函数中……在回调函数中时-显然没有当前上下文。

I've faced this issue and non of the solutions worked and Most Important thing is if you're using 我遇到了这个问题,但没有解决方案,最重要的是,如果您使用的是

async await 
OperationContext.Current; will be null

My usage is to get Ip so used it like this before any awaitable call 我的用法是在等待呼叫之前让Ip像这样使用它

var clientIpAddress = System.Web.HttpContext.Current?.Request?.UserHostAddress;

After the first await statement in your async service operation, OperationContext.Current could be null because the rest of the method body may be running on a different thread (and OperationContext does not flow between threads 在异步服务操作中的第一个await语句之后,OperationContext.Current可能为null,因为方法主体的其余部分可能正在其他线程上运行(并且OperationContext在线程之间不流动

So to get it you can write your code before any awaitable action 因此,要获得它,您可以在任何等待的操作之前编写代码

May be it'll help someone :) 可能会帮助某人:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM