Dan Rigsby - Coding Up Style

.Net, C#, & Wcf Development

Extending WCF InstanceContext to store custom state

Posted by Dan Rigsby on May 24th, 2008

InstanceContext in Wcf Series:
Part 1: Understanding InstanceContext
Part 2: Extending InstanceContext to store custom state

This is Part 2 in a series on InstanceContexts.  You might want to read Part 1: Understanding InstanceContext in WCF before continuing on with this post.

Like most things in WCF, there are a few ways to extend an InstanceContext:

  1. IExtension<InstanceContext>: This interface is used to create objects that can be attached to the InstanceContext through the Extensions property.  These objects can store additional state information or behaviors.  This works off the standard extensible object pattern is used in WCF to either extend existing run-time classes with new functionality or to add new state features to an object.
  2. IInstanceContextProvider: Implement to participate in the creation or choosing of a InstanceContext, especially to enable shared sessions.
  3. IInstanceContextInitializer: Defines the methods necessary to inspect or modify the creation of InstanceContext objects when required. This is typically added to attach an IExtension<InstanceContext> to the InstanceContext object as a mechanism for passing data throughout an application.

Lets look at an example that implements some of these extension mechanisms to store additional state information with the InstanceContext.  For this sample, I want some extension that allows me to store the DateTime that an instance of the service was created and what is the creation index count of this instance.  The goal is to just be able to add an Attribute to the service and have it automatically store these values in the InstanceContext. To do this I need an IExtension that defines the values I want to store, an IInstanceContextProvider that adds the IExtension to the service instance when it is created, and an Attribute that implements IContractBehavior that we can use on the service.

1. IExtension<InstanceContext>

The IExtension interface is part of the extensible object pattern is used in WCF to either extend existing run-time classes with new functionality or to add new state features to an object. Since InstanceContext derives from IExtensibleObject, we can add these extension controls to it in order to provide more state information with the InstanceContext.  This interface defines 2 methods:

  1. void Attach(T owner): Enables an extension object to find out when it has been aggregated. Called when the extension is added to the IExtensibleObject<T>Extensions property.
  2. void Detach(T owner): Enables an object to find out when it is no longer aggregated. Called when an extension is removed from the IExtensibleObject<T>Extensions property.

In this example we are only using the extension to hold data and do not care what happens when it is attached or detached from the owner.  So we only need to define the two properties we want to track:

[DataContract]
public class InstanceCreationExtension :
    IExtension<InstanceContext>
{
    private int m_InstanceCreationIndex;
    private DateTime m_InstanceCreated;

    [DataMember]
    public int InstanceCreationIndex
    {
        get { return m_InstanceCreationIndex; }
    }

    [DataMember]
    public DateTime InstanceCreated
    {
        get { return m_InstanceCreated; }
    }

    public InstanceCreationExtension(
        int instanceCreationIndex,
        DateTime instanceCreated)
    {
        m_InstanceCreationIndex = instanceCreationIndex;
        m_InstanceCreated = instanceCreated;
    }

    public override string ToString()
    {
        return String.Format("Creation Index {0}, Created: '{1}'",
            m_InstanceCreationIndex,
            m_InstanceCreated);
    }

    public void Attach(
        InstanceContext owner)
    {
    }

    public void Detach(
        InstanceContext owner)
    {
    }
}

2. IInstanceContextInitializer

The IInstanceContextInitializer interfacedDefines the methods necessary to inspect or modify the creation of InstanceContext objects.  When an InstanceContext is created, this interface can be used to perform additional operations on it through the Initialize method.  In our example, we want to add a our custom IExtension object to the Extensions collection when the InstanceContext is created:

public class InstanceCreationInitializer :
    IInstanceContextInitializer
{
    private static int m_InstanceCreationIndex = 0;

    public void Initialize(
        InstanceContext instanceContext,
        Message message)
    {
        // Increment instance creation index (thread safe)
        System.Threading.Interlocked.Increment(
            ref m_InstanceCreationIndex);

        // Add extension which contains the new instance creation index
        instanceContext.Extensions.Add(
            new InstanceCreationExtension(
                m_InstanceCreationIndex,
                DateTime.Now));
    }
}

3. IContractBehavior Attribute

Now that we have defined an object to store our data and a way to populate it, we have to wire up our custom IInstanceContextInitializer so that it can do its job.  There are a few ways to do this, but perhaps the easiest way is to create a custom attribute that can be added to the service which wires it up.  To do this, we just need to extend the IContractBehavior interface. This interface defines the methods that can be used to extend run-time behavior for a contract in either a service or client application.  For this example, we only need to modify the dispatch behavior that is used or the service.  In this method we are going to added our custom IInstanceContextInilializer to the InstanceContextInitializers collection on the DispatchRuntime.  This basically means that when this attribute is applied to a service, then every time an instance is created, it will call the custom IInstanceContextInilializer that we have defined.

public class InstanceCreationAttribute :
    Attribute, IContractBehavior
{
    public void AddBindingParameters(
        ContractDescription contractDescription,
        ServiceEndpoint endpoint,
        BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(
        ContractDescription contractDescription,
        ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(
        ContractDescription contractDescription,
        ServiceEndpoint endpoint,
        DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceContextInitializers.Add(
            new InstanceCreationInitializer());
    }

    public void Validate(
        ContractDescription contractDescription,
        ServiceEndpoint endpoint)
    {
    }
}

Contract

We now have all of the customizations we need defined.  Now we can see how it is applied to a service.  For this example, we can define a contract with a single method that returns a string which will show us the service instance creation count and the DateTime in which the service was created:

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    string GetInstanceDescription();
}

Service

The service is pretty straight forward.  The implementation of the GetInstanceDescription method just pulls our custom IExtension from the Extensions collection of the current InstanceContext that the operation is running on.  Notice that we have set the InstanceContextMode to PerCall.  This means that every time we call a method a new instance of a service will be created.  So this method should return a unique string every time.  (You can change this to PerSession and Single to see different results.)

The real magic that gets our example working is the [InstanceCreation] attribute we defined on the service.  This is the custom IContractBehavior attribute that will inject our custom IInstanceContextInitializer and add our custom IExtension object to the InstanceContext.

[ServiceBehavior(
    InstanceContextMode=InstanceContextMode.PerCall)]
[InstanceCreation]
public class MyService : IMyService
{
    public string GetInstanceDescription()
    {
        InstanceContext ic = OperationContext.Current.InstanceContext;

        InstanceCreationExtension extension =
            ic.Extensions.Find<InstanceCreationExtension>();

        return extension.ToString();
    }
}

Example Client

In this sample client we are going to just call the method on the service and display a MessageBox with data from the InstanceContext on the service.  Since we have the service InstanceContextMode set as PerCall, this message will be different each time you click the button on the form.

public partial class ClientForm : Form
{
    IMyService m_Client;

    public ClientForm()
    {
        InitializeComponent();

        ChannelFactory<IMyService> channeFactory =
            new ChannelFactory<IMyService>("NetTcpBinding_IMyService");
        m_Client = channeFactory.CreateChannel();
    }

    private void m_AddNameButton_Click(object sender, EventArgs e)
    {
        MessageBox.Show(m_Client.GetInstanceDescription());
    }
}

 

You can use this sample to add any type of information you want to each instance of a service.  This is most useful if you are using PerCall or PerSession InstanceContextMode.  You could have done some of these operations in the service constructor, but this type of extension allows you to bury this logic into a reusable component and access data that may not normally have access to.

You can download a complete sample of the code discussed in this article here: http://www.danrigsby.com/Files/Rigsby.WcfServiceContexts.zip

 

kick it on DotNetKicks.com

5 Responses to “Extending WCF InstanceContext to store custom state”

  1. Dan Rigsby » Understanding InstanceContext in WCF Says:

    [...] Clarkin « XNA Game Studio 3.0 CTP, no 64-Bit support (yet) Extending WCF InstanceContext to store custom state [...]

  2. Arnon Rotem-Gal-Oz Says:

    Yes, you can use InstanceContext to store state - but it is easier to mark the service with  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] and achieve the same effect
    Â
    Arnon

  3. Dan Rigsby Says:

    Making a Singe instance isnt always the best option, but sometimes it is (this might be a good future article itself).  As this article noted, these extensions are most useful for PerCall or PerSession.   Of course its always easy to store these values in local fields on the object itself too, but this allows you to bury potentially complex logic (depending on what you are doing) into reusable component.

  4. Dew Drop - May 25, 2008 | Alvin Ashcraft's Morning Dew Says:

    [...] Extending WCF InstanceContext to Store Custom State (Dan Rigsby) - Part 2 in Dan’s series on WCF InstanceContext. [...]

  5. Weekly microsoft oriented crumbs #6 - ASP.net MVC Preview 3 - Service Endpoint Says:

    [...] to make your LINQ span the globe, linq extensions that make a link query run on multiple machines Dan Rigsby - Extending WCF InstanceContext to store custom state, WCF [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>