none
OperationContext in MultiThreaded application

    Question

  • Hi,

    I am constructing a WCF service and I need an object should be available throughout the process. I am using several components(For excpeion, logging etc) and i need the same object in these components too. Initially i thought of using Sessions which were replaced by OperationContext IncomingMessageProperties. When i receive the request, i am adding my object to incoming message properties. Now my question is, I am using multiple threads for exception handling and logging, how could i get the property which i added to  OpearionContext.Current.IncomingMessageProperties, in these methods which were invoked in different threads?

    Monday, July 26, 2010 9:14 AM

Answers

  • Hello,

    you can try to use CallContext. It's supposed to be used in .NET remoting but it should also work for your scenario.

    Simple console test:

    using System;
    using System.Threading;
    using System.Runtime.Remoting.Messaging;
    
    class Program
    {
      static void Main(string[] args)
      {
        // Shared data object is passed to CallContext
        var data = new DataObject() { Message = "Message", Status = "Status" };
        CallContext.SetData("data", data);
    
        var threads = new Thread[5];
    
        for (int i = 0; i < threads.Length; i++)
        {
          threads[i] = new Thread(Process);
          threads[i].Start();
        }
    
        // Change to shared object can affect threads which hasn't been processed yet
        data.Message = "No message";
        Process();
    
        for (int i = 0; i < threads.Length; i++)
        {
          threads[i].Join();
        }
    
        Console.ReadLine();
      }
    
      static void Process()
      {
        // Retrieving shared data from CallContext
        var data = CallContext.GetData("data") as DataObject;
        Console.WriteLine("{0}: Message: {1}; Status: {2}", Thread.CurrentThread.ManagedThreadId, data.Message, data.Status);
      }
    }
    
    // Shared data object has to implement ILogicalThreadAffinative
    public class DataObject : ILogicalThreadAffinative
    {
      public string Message { get; set; }
      public string Status { get; set; }
    }
    
    Best regards,
    Ladislav

     

    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 12:00 PM
  • Hi Fayaz,

    You cannot get OperationContext on different threads. If you want to share data between serviceoperation and the seperate threads, you may try to create static property in Service. Here is an sample code using static queue to share data, please have a test.

        public class Service1 : IService1
        {

            // use static queue to share data.
            static Queue<int> queue = new Queue<int>();

            // create separate thread to log info
            static Service1()
            {
                ThreadPool.QueueUserWorkItem(
                    new WaitCallback(MyFunc));
            }

            public string GetData(int value)
            {
                lock (queue)
                {
                    queue.Enqueue(value);
                }
                return string.Format("You entered: {0}", value);
            }

            static void MyFunc(object o)
            {
                while(true)
                {
                    lock(queue)
                    {
                        if(queue.Count>0)
                        {
                            int value = queue.Dequeue();
                            System.Diagnostics.Trace.TraceInformation("Message received:" + value.ToString());
                        }
                        Thread.Sleep(1000);
                    }
                }
            }
        }

    Thanks,


    Mog Liang
    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 5:42 AM
  • You could use the following approach. First we specify a class that will be holding the state object (using the singleton pattern), i.e. the current operation context:

      class OperationContextState
      {
        private static OperationContextState _state;
        private static object _snycLock = new object();
        private OperationContext _context;
    
        private OperationContextState()
        {      
        }
    
        public static OperationContextState Instance
        {
          get
          {
            if (_state == null)
            {
              lock (_snycLock)
              {
                if (_state == null)
                  _state = new OperationContextState();            
              }
            }
    
            return _state;
          }
        }
    
        public OperationContext OpContext 
        {
          get
          {
            lock (_snycLock)
            {
              return _context;
            }
          }
          set
          {
            lock (_snycLock)
            {
              _context = value;
            }
          }
        }
      }
    

    Then, we specify the extension for the operation context:

      class OperationContextStateExtension : IExtension<OperationContext>
      {
        public void Attach(OperationContext context)
        {
          OperationContextState.Instance.OpContext = context;
          context.OperationCompleted += (s, a) => this.Detach(context);
        }
    
        public void Detach(OperationContext context)
        {
          OperationContextState.Instance.OpContext = null; 
        }    
      }
    

    Note here that Attach and Detach methods are called on context creation and termination so the state gets updated properly.

    The extension must be attached like so:

      OperationContext.Current.Extensions.Add(new OperationContextStateExtension());
    

    Then you can access the state object from any thread like so:

      OperationContextState.Instance.OpContext
    

    What do you think about this approach?

     

    Regards,

    Stipe Ivan

    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 12:03 PM

All replies

  • You can fetch the object the same way you stored it - by using the OperationContext.Current.IncomingMessageProperties property. The threads are started in the same app domain, meaning that they have access to the same memory space - this all means that you can access the current operation context without a problem. If this didn't answer your question, feel free to discuss it some more.

    Regards,

    Stipe Ivan

    Tuesday, July 27, 2010 1:00 AM
  • Hi Stipe Ivan,

    Thanks for reply. I read OperationContext as ThreadStatic. Once you start a new thread, operation context is gone. That's why I consider this as an issue. I will test and give you detailed explanation.

    Referring to

    http://weblogs.asp.net/avnerk/archive/2006/05/18/OperationContext-is-ThreadStatic.aspx

    Tuesday, July 27, 2010 3:56 AM
  • Hi again. As mentioned  OperationContext.Current is null in new thread. How could I get my newly added property. I can not pass context to my method, since there are hundreds of methods and i need to change all probably by having overloaded method for passing context. Is there any alternate approach?
    Tuesday, July 27, 2010 4:30 AM
  • Hi Fayaz,

    You cannot get OperationContext on different threads. If you want to share data between serviceoperation and the seperate threads, you may try to create static property in Service. Here is an sample code using static queue to share data, please have a test.

        public class Service1 : IService1
        {

            // use static queue to share data.
            static Queue<int> queue = new Queue<int>();

            // create separate thread to log info
            static Service1()
            {
                ThreadPool.QueueUserWorkItem(
                    new WaitCallback(MyFunc));
            }

            public string GetData(int value)
            {
                lock (queue)
                {
                    queue.Enqueue(value);
                }
                return string.Format("You entered: {0}", value);
            }

            static void MyFunc(object o)
            {
                while(true)
                {
                    lock(queue)
                    {
                        if(queue.Count>0)
                        {
                            int value = queue.Dequeue();
                            System.Diagnostics.Trace.TraceInformation("Message received:" + value.ToString());
                        }
                        Thread.Sleep(1000);
                    }
                }
            }
        }

    Thanks,


    Mog Liang
    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 5:42 AM
  • Hi Mog Liang,

    Thanks for reply. Sorry for my ignorance, I dint understood the code. Actually, I need an object should be available in all components used in service. This object data differs for different calls. If I am queuing it, what happens if thousand of calls occurred at same time? As I said, i need this object in different component. How could I access it from this code?

    Tuesday, July 27, 2010 6:31 AM
  • Hello,

    if you start new threads from main thread which is serving the service request you can use ParametrizedThreadStart to pass data to new threads. In that case you just create the array of object or specialized type which will contain your logging componenent, exception handling component and etc. and pass that array / object to the new thread.

    http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx

    Best regards,
    Ladislav

    Tuesday, July 27, 2010 6:47 AM
  • Hello,

    if you start new threads from main thread which is serving the service request you can use ParametrizedThreadStart to pass data to new threads. In that case you just create the array of object or specialized type which will contain your logging componenent, exception handling component and etc. and pass that array / object to the new thread.

    http://msdn.microsoft.com/en-us/library/system.threading.parameterizedthreadstart.aspx

    Best regards,
    Ladislav


    Yes, that will work. But, that leads me to a huge code change almost complete code change. I am looking for any short alternate approach...
    Tuesday, July 27, 2010 7:30 AM
  • Ups, didn't know the context was marked as thread static. I have a few ideas on how to solve your issue, but could you first elaborate a bit more on your scenario (when the object should be created, what about its lifetime, do you edit the state during calls, etc.)? 

    Regards,

    Stipe Ivan

    Tuesday, July 27, 2010 9:39 AM
  • The very first step when i get request, i build an object. This object is having request id and other info. In this service I am using different components like service library, exception handling, logging. service invokes service library method. Service library is responsible for creating threads for logging and exception handling to avoid unnecessary wait for additional processing. The object will not be edited once it is set and which is set only once at the very beginning of the request. The lifetime of the object is only that call(the complete processing). The service is having high traffic, thousands of calls for service processing. Initially i used HttpContext Session, and i am asked to use OperationContext or any other approach rather than Session.
    Tuesday, July 27, 2010 10:18 AM
  • Hello,

    so your former solution was based on assumtion that two concurrent calls from the same client never arrives. Session is shared between all calls so if two concurrent calls arrives the object in session will be overwritten by second call and first call will work with that object as well.

    Btw. you can use HttpContext and Session in WCF as well if you turn on AspNetCompatibilityMode.
    http://blogs.msdn.com/b/sajay/archive/2006/08/03/687361.aspx

    Best regards,
    Ladislav

    Tuesday, July 27, 2010 10:39 AM
  • Yes the assumption is correct. There wont be any concurrent calls form same client. And I know the solution with Session. But I don't want to use them.
    Tuesday, July 27, 2010 10:47 AM
  • Hello,

    you can try to use CallContext. It's supposed to be used in .NET remoting but it should also work for your scenario.

    Simple console test:

    using System;
    using System.Threading;
    using System.Runtime.Remoting.Messaging;
    
    class Program
    {
      static void Main(string[] args)
      {
        // Shared data object is passed to CallContext
        var data = new DataObject() { Message = "Message", Status = "Status" };
        CallContext.SetData("data", data);
    
        var threads = new Thread[5];
    
        for (int i = 0; i < threads.Length; i++)
        {
          threads[i] = new Thread(Process);
          threads[i].Start();
        }
    
        // Change to shared object can affect threads which hasn't been processed yet
        data.Message = "No message";
        Process();
    
        for (int i = 0; i < threads.Length; i++)
        {
          threads[i].Join();
        }
    
        Console.ReadLine();
      }
    
      static void Process()
      {
        // Retrieving shared data from CallContext
        var data = CallContext.GetData("data") as DataObject;
        Console.WriteLine("{0}: Message: {1}; Status: {2}", Thread.CurrentThread.ManagedThreadId, data.Message, data.Status);
      }
    }
    
    // Shared data object has to implement ILogicalThreadAffinative
    public class DataObject : ILogicalThreadAffinative
    {
      public string Message { get; set; }
      public string Status { get; set; }
    }
    
    Best regards,
    Ladislav

     

    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 12:00 PM
  • You could use the following approach. First we specify a class that will be holding the state object (using the singleton pattern), i.e. the current operation context:

      class OperationContextState
      {
        private static OperationContextState _state;
        private static object _snycLock = new object();
        private OperationContext _context;
    
        private OperationContextState()
        {      
        }
    
        public static OperationContextState Instance
        {
          get
          {
            if (_state == null)
            {
              lock (_snycLock)
              {
                if (_state == null)
                  _state = new OperationContextState();            
              }
            }
    
            return _state;
          }
        }
    
        public OperationContext OpContext 
        {
          get
          {
            lock (_snycLock)
            {
              return _context;
            }
          }
          set
          {
            lock (_snycLock)
            {
              _context = value;
            }
          }
        }
      }
    

    Then, we specify the extension for the operation context:

      class OperationContextStateExtension : IExtension<OperationContext>
      {
        public void Attach(OperationContext context)
        {
          OperationContextState.Instance.OpContext = context;
          context.OperationCompleted += (s, a) => this.Detach(context);
        }
    
        public void Detach(OperationContext context)
        {
          OperationContextState.Instance.OpContext = null; 
        }    
      }
    

    Note here that Attach and Detach methods are called on context creation and termination so the state gets updated properly.

    The extension must be attached like so:

      OperationContext.Current.Extensions.Add(new OperationContextStateExtension());
    

    Then you can access the state object from any thread like so:

      OperationContextState.Instance.OpContext
    

    What do you think about this approach?

     

    Regards,

    Stipe Ivan

    • Marked as answer by fayaz_3e Thursday, July 29, 2010 12:23 PM
    Tuesday, July 27, 2010 12:03 PM
  • Thanks a lot for your replies. I will test it and will let you know the results tomorrow. Sleeping time for me. Thanks once again :)
    Tuesday, July 27, 2010 12:10 PM
  • Hello,

    you can try to use CallContext. It's supposed to be used in .NET remoting but it should also work for your scenario.

    Simple console test:

    using
     System;
    using System.Threading;
    using System.Runtime.Remoting.Messaging;

    class Program
    {
    static void Main(string [] args)
    {
    // Shared data object is passed to CallContext
    var data = new DataObject() { Message = "Message" , Status = "Status" };
    CallContext.SetData("data" , data);

    var threads = new Thread[5];

    for (int i = 0; i < threads.Length; i++)
    {
    threads[i] = new Thread(Process);
    threads[i].Start();
    }

    // Change to shared object can affect threads which hasn't been processed yet
    data.Message = "No message" ;
    Process();

    for (int i = 0; i < threads.Length; i++)
    {
    threads[i].Join();
    }

    Console.ReadLine();
    }

    static void Process()
    {
    // Retrieving shared data from CallContext
    var data = CallContext.GetData("data" ) as DataObject;
    Console.WriteLine("{0}: Message: {1}; Status: {2}" , Thread.CurrentThread.ManagedThreadId, data.Message, data.Status);
    }
    }

    // Shared data object has to implement ILogicalThreadAffinative
    public class DataObject : ILogicalThreadAffinative
    {
    public string Message { get ; set ; }
    public string Status { get ; set ; }
    }
    Best regards,
    Ladislav

     

    Hi Ladislav,

    CallContext is working exactly how am expected. Thank you. Never had an experience in remoting, is it OK to use CallContext in WCF? Does it have any negative impact on my service? Since remoting concept is .Net specific, is it correct to use it in WCF since the service consumers may be java or some other platform?

    Wednesday, July 28, 2010 7:15 AM
  • You could use the following approach. First we specify a class that will be holding the state object (using the singleton pattern), i.e. the current operation context:

     

     class
     OperationContextState
     {
      private
     static
     OperationContextState _state;
      private
     static
     object
     _snycLock = new
     object
    ();
      private
     OperationContext _context;
    
      private
     OperationContextState()
      {   
      }
    
      public
     static
     OperationContextState Instance
      {
       get
    
       {
        if
     (_state == null
    )
        {
         lock
     (_snycLock)
         {
          if
     (_state == null
    )
           _state = new
     OperationContextState();      
         }
        }
    
        return
     _state;
       }
      }
    
      public
     OperationContext OpContext 
      {
       get
    
       {
        lock
     (_snycLock)
        {
         return
     _context;
        }
       }
       set
    
       {
        lock
     (_snycLock)
        {
         _context = value;
        }
       }
      }
     }
    

     

    Then, we specify the extension for the operation context:

     

     class
     OperationContextStateExtension : IExtension<OperationContext>
     {
      public
     void
     Attach(OperationContext context)
      {
       OperationContextState.Instance.OpContext = context;
       context.OperationCompleted += (s, a) => this
    .Detach(context);
      }
    
      public
     void
     Detach(OperationContext context)
      {
       OperationContextState.Instance.OpContext = null
    ; 
      }  
     }
    

     

    Note here that Attach and Detach methods are called on context creation and termination so the state gets updated properly.

    The extension must be attached like so:

     

     OperationContext.Current.Extensions.Add(new
     OperationContextStateExtension());
    

     

    Then you can access the state object from any thread like so:

     

     OperationContextState.Instance.OpContext
    

     

    What do you think about this approach?

     

    Regards,

    Stipe Ivan


    Hi Stipe Ivan,

    Could you please elaborate your explanation? Am afraid of singleton, since it uses static approach. What happens when multiple users are invoking the service at same time, how the implementation knows which OperationContext object should be assigned to which request? And am afraid we did lock Instance and OpContext in OperationContextState. Since it is singleton, one object is available at a time, if two requests should be processed, then where the other object is maintained? Could you please explain me the code how it works if multiple request should be processed at same time?  I appreciate your help. Thanks.

    Wednesday, July 28, 2010 7:25 AM
  • Hello,

    I think there should not be any problem but you should test it with java client. I have tested it with .NET client configured to use BasicHttp, WSHttp and NetTcp bindings and it worked.

    Best regards,
    Ladislav

    Wednesday, July 28, 2010 8:24 AM