A simple-ish approach to custom context in WCF

In WCF, we have this thing called OperationContext.Current that carries lots of useful information about the 'current' service operation being processed. You can use this on the server to fish out the ServiceEndpoint that the message you're processing came in on. On the client, you can use OperationContext.Current to do things like adding custom headers to the outgoing message. All in all, OperationContext.Current is the place to go for WCF-specific context about the current operation.

It's pretty common to want to extend this patten to cover app-specific context as well (see this post on the WCF forums, which inspired me to write this blog entry). OperationContext implements the IExtensibleObject<T> pattern, so you can hang additional things off of the OperationContext.Current.Extensions. To make that happen, you need two to do two things:

  1. Create a custom class to store your contextual data.
  2. Implement IExtension<OperationContext> on that class.

Here's the simplest thing that could possibly work:

    public class MyContext : IExtension<OperationContext>

    {

        //The "current" custom context

        public static MyContext Current

        {

            get { return OperationContext.Current.Extensions.Find<MyContext>(); }

        }

 

        #region IExtension<OperationContext> Members

 

        public void Attach(OperationContext owner)

        {

            //no-op

        }

 

        public void Detach(OperationContext owner)

        {

            //no-op

        }

        #endregion

 

        string contextProperty1;

 

        //You can have lots more of these -- this is the stuff that you

        //want to store on your custom context

        public string ContextProperty1

        {

            get { return this.contextProperty1; }

            set { this.contextProperty1 = value; }

        }

    }

IExtension<OperationContext> has two members -- Attach() and Detach() that get called when your object gets added and removed to the Extensions collection. I've no-op'd them here for simplicity, but this is a nice place to do some custom intialization/deinitialization logic if you need to do stuff like that.

The way you get an extension out of the OperationContext is by calling OperationContext.Current.Extensions.Find<TContext>(). I added a static Current property to MyContext to make this a little easier (now we can do MyContext.Current just like we can do OperationContext.Current).

An easy way to get an extension in to the Extensions collection is with a Message Inspector:

    public class MyContextMessageInspector : IDispatchMessageInspector

    {

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
                                          IClientChannel channel,
                                          InstanceContext instanceContext)

        {

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

            return request.Headers.MessageId;

        }

 

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)

        {

            OperationContext.Current.Extensions.Remove(MyContext.Current);

        } 

    }

 

The way you add a Message Inspector to all the endpoints on a service is through IServiceBehavior:

    public class MyContextBehaviorAttribute : Attribute, IServiceBehavior

    {

        #region IServiceBehavior Members

 

        public void AddBindingParameters(ServiceDescription serviceDescription,

                                         ServiceHostBase serviceHostBase,

                                         System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,        

                                         BindingParameterCollection bindingParameters)

        {

            //no-op

        }

 

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

        {

            foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)

            {

                foreach (EndpointDispatcher ed in cd.Endpoints)

                {

                    ed.DispatchRuntime.MessageInspectors.Add(new MyContextMessageInspector());

                }

            }

        }

 

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)

        {

            //no-op           

        }

        #endregion

    }

I implemented this on a custom Attribute so we can do things like this:

    [ServiceContract]

    [MyContextBehavior]

    public class MySerivce

    {

        [OperationContract]

        public string DoStuff( string foo )

        {

            //Use MyContext.Current here to do something interesting...

            string bar = MyContext.Current.ContextProperty1;

        }

    }

This demonstrates how to do a simple per-request context, where you just need to stick stuff on the thread before your operation implementation gets called. If you need to do more advanced stuff (like sharing the same context across multiple calls), you might look at implementing your own InstanceContext as that's a little bit more advanced.