current OperationContext is null in WCF Windows Service

27,837

Solution 1

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 provides an OperationContext instance when your operation is running within a service.

Solution 2

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

Solution 3

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

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

So to get it you can write your code before any awaitable action

May be it'll help someone :)

Share:
27,837
dtaylor
Author by

dtaylor

Applications development

Updated on July 30, 2022

Comments

  • dtaylor
    dtaylor almost 2 years

    I am trying to set up a Publish/Subscribe system using WCF and where the WCF server is in a Windows service. The binding is 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. In the Subscribe method I attempt to get the callback channel using the OperationContext.Current.GetCallbackChannel method. When I attempt this the OperationContext.Current property returns NULL.

    Can anyone tell me under what circumstances this property would return 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.

    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. 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. 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. 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:

        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:

        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:

        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:

        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; }
        }
    }
    
  • dtaylor
    dtaylor about 11 years
    This was the correct answer. When I created an instance of the service in the client instead of an instance of the proxy, I went merrily on my way calling methods on my service not realizing it was the wrong instance of the service. The only thing that stopped me was the absence of the OperationContext. Thanks again.
  • dtaylor
    dtaylor about 11 years
    Milan - you are correct. Unfortunately, Ian had figured this out in the comments of the question yesterday and I can only mark one answer as "answered".