Skip to content

WCF and ActiveRecord, let’s be friends!

December 6, 2012

For the coders out there that follow this blog, you’ll probably know the KitchenPC back-end is completely built on Castle ActiveRecord, an easy to use ORM for .NET build on NHibernate.  I’m a huge fan, even though the project is no longer active and most people are probably using Fluent NHibernate or Microsoft’s own ORM, the Entity Framework (blech).  I also happen to be a huge fan of the ActiveRecord pattern in general, so I’m sticking with it and you can’t make me change.

Getting WCF to play nicely with ActiveRecord turned out to be somewhat of a time vampire; not because the solution is hugely complicated, but because it required me to dig in to the inner workings of both WCF and ActiveRecord.  I also found almost zero information out there covering integration between these two technologies, as most blog posts and articles focus on NHibernate itself and may or may not apply to ActiveRecord specific implementation details.  Posting questions on StackOverflow also yielded nothing but cricket noises.

For that reason, I decided to write a technical blog post illustrating exactly how to get these guys to be friends.  Turns out, it’s not all that scary!

Assumptions:

First, I will assume the reader is already family with Castle ActiveRecord, and knows how to define mappings, initialize the framework in Application_Start, and has the basics down of using the framework.  If not, I’d suggest reading up on Castle ActiveRecord, following a few tutorials, and trying it out first on a simple ASP.NET web site.

I’ll also assume you have some basic knowledge of WCF.  However, if you want to be really well versed in the extensibility story for WCF, I highly suggestion reading this article.

ActiveRecord Scopes

NHibernate, for reasons of efficiency, works based on sessions.  To avoid running tons of SQL statements after every line of code you write, the underlying engine will batch things up and run a bunch of statements at once when it’s wise to do so.  It’s also smart enough to know when it needs to re-query data, commit pending transactions first, or invalidate cached data.  SessionScopes can be nested, which means internally they’re represented in a stack of SessionScope objects.

A SessionScope object tracks a collection of pending queries, and commits them to a database (provided everything is kosher) when the object is disposed.  A very simple example of this might be:

   using (new SessionScope())
   {
      Recipe r = Recipe.Find(123);
      r.PrepTime = 60;
      r.Ingredients.Add(new Ingredient(5));

      r.Update();
   }

Here, we find a Recipe object in the database with the ID of 123, set a new prep time for that recipe, then add a new ingredient to the recipe’s ingredient collection. NHibernate, under the covers, is tracking what objects have changed and issuing UPDATE commands through the database provider when SessionScope is disposed.  If we throw an exception halfway through, things we already updated won’t actually get changed, as those UPDATE commands will never be sent.

One thing you’ll notice is that I don’t actually refer to my SessionScope object anywhere. You might be asking yourself how r.Update() knows which session we’re currently in. Well, this is done through an implementation of IThreadScopeInfo. When you call SessionScope.Current, the Castle framework calls upon the configured IThreadScopeInfo which is responsible for finding the current session.

Castle ActiveRecord ships with a few of these. One is called WebThreadScopeInfo and stores the current session stack in the HttpContext.Current.Items[] collection. This means that the session is scoped to each individual HTTP request. Another implementation is HybridWebThreadScopeInfo, which allows you to run sessions in any random ol’ thread, where HttpContext.Current would be null. The HybridWebThreadScopeInfo class will first check HttpContext.Current, and if it’s null, return a session stack keyed to the current thread, creating a new stack if necessary.

This design allows you to call functions which call other functions which call other functions without having to worry about passing sessions and scopes all over the place.

Castle ActiveRecord actually makes this even easier. There exists an HTTP module called SessionScopeWebModule which runs before each HTTP request, creates a new SessionScope for that request, and disposes of it when the HTTP request ends. Using this module, you can write the above code as just:

   Recipe r = Recipe.Find(123);
   r.PrepTime = 60;
   r.Ingredients.Add(new Ingredient(5));

   r.Update();

There’s no need to new up a SessionScope, as one has been created for you by SessionScopeWebModule. Pretty cool, eh?

So why doesn’t this solution work in WCF?

As I pointed out in my previous post, the WCF stack is completely independent of ASP.NET. HTTP modules won’t be run and there’s no HttpContext.Current.Items collection to store the active session stack. For this reason, you’ll need to re-create a few of these solutions in a way that’s compatible with WCF’s architecture.  You can also run in aspNetCompatibilityEnabled mode, but that has its own set of limitations.

Unfortunately, Castle ActiveRecord doesn’t have support for this out of the box, but it offers the extensibility to design a solution similar to the ASP.NET handlers. In fact, I based my code completely off the built in SessionScopeWebModule HTTP module and HybridWebThreadScopeInfo scope info class.

Step 1: Creating a scope for each WCF request

In ASP.NET, this would be done using an httpModule.  In WCF, this is done by implementing a message inspector.  A message inspector, which is an implementation of IDispatchMessageInspector, is a class that can look at a message traveling through the WCF pipeline and do things before and after the message is processed.  It’s dead simple, and has an AfterReceiveRequest method which is called after the request is received and deserialized, and a BeforeSendReply which is called before the reply is assembled.  We can use this to create a new session scope for our WCF operations.

public class MyInspector : IDispatchMessageInspector
{
   public MyInspector()
   {
   }

   public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
   {
      return new SessionScope();
   }

   public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
   {
      SessionScope scope = correlationState as SessionScope;

      if (scope != null)
      {
         scope.Dispose();
      }
   }
}

Ok so what’s going on here? When we receive a request, we new up a SessionScope object. The constructor actually adds itself to the current session stack through the configured IThreadScopeInfo, so all that is taken care of for us. This is actually quite similar to what the web module does, only the web module adds that SessionScope object to the HttpContext.Current.Items collection so the scope can be disposed of later.  We don’t need to that because we take advantage of correlation state.  Any object AfterReceiveRequest returns will be passed in to BeforeSendReply, which is useful for tracking objects in a message inspector through the lifespan of a WCF request.

You’ll notice that we use this concept in BeforeSendReply to grab the reference to the SessionScope created above and dispose of it, thus committing any pending transactions to the database.

This inspector now has to be attached to a WCF service. This can be done in a variety of ways (mostly mucking with your web.config), but perhaps the easiest is by implementing a custom IServiceBehavior. An IServiceBehavior defines a custom behavior for a service, such as adding custom message inspectors. A very cool feature of this is your IServiceBehavior can derive from the .NET Attribute class which allows you to tag a WCF service directly with a behavior, no need to mess around with configuration files. This class is quite simple to implement:

public class MyServiceBehaviorAttribute : Attribute, IServiceBehavior
{
   public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   {
   }

   public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
   {
      foreach (ChannelDispatcher cDispatcher in serviceHostBase.ChannelDispatchers)
         foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
            eDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector());

   }

   public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
   {
   }
}

Nothing to really explain here. When the behavior is applied, it goes through all the endpoints that connect to that service and applies our message inspector. You can then tag your WCF service with that behavior:

[MyServiceBehavior]
public class Service1 : IService1
{
   // Your operation contracts here
}

When we have this hooked up, every request to your service will be gauranteed to have its own session scope, so no need to new up a SessionScope object yourself. It’ll work just like ASP.NET using the SessionScopeWebModule module.

Well crap, that won’t actually work…

If you’re smart and/or know a lot about IIS, you’ll see a major problem with this. WCF exhibits thread agility, which mean the message inspector (where our SessionScope was created) and the service operation (where we’ll be looking for SessionScope.Current) could be different threads!

As I mentioned before, the current SessionScope is resolved through a IThreadScopeInfo implementation. The WebThreadScopeInfo implementation uses HttpContext.Current to track the session stack, providing an immunity against ASP.NET thread agility. The HybridWebThreadScopeInfo implementation attempts to use HttpContext.Current, but if it’s not found it basically creates a stack keyed by the local thread. This kinda works for simple cases, like loading some data into memory within an initialization thread, but probably isn’t very smart for us. If we need to lazy load some property and a session scope had never been created on that thread, we’re going to crash with the exception: “failed to lazily initialize a collection of role: xxx, no session or session was closed”.  This would of course happen intermittently, and only in the middle of the night or on your vacation.  You’ll never be able to reproduce it on your dev box, and it would make you hate the world and yearn for a simpler time before all this technology.

The solution? Write a super-duper-hybrid session scope info class. One that does it all. This thing of beauty will check the HttpContext.Current if we’re running on an ASP.NET page, check the WCF OperationContext.Current if we’re in an WCF operation, and then fall back to a stack keyed by the thread if all else fails. It will slice, it will dice, you’ll be seeing infomercials for this IThreadScopeInfo. And I won’t charge you three payments of $19.95 to use it. In fact, I’ll give you the code for free.

This implementation is heavily based on the HybridWebThreadScopeInfo implementation, which I recommend skimming over first. I just added a bit of extra logic to handle the WCF case.

public class WcfThreadScopeInfo : AbstractThreadScopeInfo, IWebThreadScopeInfo
{
   const string ActiveRecordCurrentStack = "activerecord.currentstack";

   [ThreadStatic]
   static Stack stack;

   public override Stack CurrentStack
   {
      [MethodImpl(MethodImplOptions.Synchronized)]
      get
      {
         Stack contextstack;

         if (HttpContext.Current != null) //We're running in an ASP.NET context
         {
            contextstack = HttpContext.Current.Items[ActiveRecordCurrentStack] as Stack;
            if (contextstack == null)
            {
               contextstack = new Stack();
               HttpContext.Current.Items[ActiveRecordCurrentStack] = contextstack;
            }

            return contextstack;
         }

         if (OperationContext.Current != null) //We're running in a WCF context
         {
            NHibernateContextManager ctxMgr = OperationContext.Current.InstanceContext.Extensions.Find<NHibernateContextManager>();
            if (ctxMgr == null)
            {
               ctxMgr = new NHibernateContextManager();
               OperationContext.Current.InstanceContext.Extensions.Add(ctxMgr);
            }

            return ctxMgr.ContextStack;
         }

         //Working in some random thread
         if (stack == null)
         {
            stack = new Stack();
         }

         return stack;
      }
   }
}

At first glance, it looks similar to the HybridWebThreadScopeInfo code, because, well, it is. It has a ThreadStatic Stack field, it checks the HttpContext.Current first, blah blah. The difference is the part in the middle which checks OperationContext.Current. This property will have a value if we’re running within the WCF pipeline. An OperationContext is analogous to HttpContext, but only for the WCF world. Similar to HttpContext.Current.Items, we can even store random junk we want to have for the lifespan of the operation. However, rather than just being a simple key/value pair like .Items is, they had to make it a bit more complicated and use something called Extensions.

An extension is any class that derives from IExtension<T>. Our extension, of course, needs to store the current SessionScope stack. Implementing it is pretty straight forward.

public class NHibernateContextManager : IExtension<InstanceContext>
{
   public Stack ContextStack { get; private set; }

   public NHibernateContextManager()
   {
      this.ContextStack = new Stack();
   }

   public void Attach(InstanceContext owner)
   {
   }

   public void Detach(InstanceContext owner)
   {
   }
}

You’ll notice IExtension<T> has Attach<T> and Detach<T> methods that are called by WCF when an extension is added or removed from the operation context, which we need to implement with dummy methods to satisfy the interface.

We can configure ActiveRecord to use this awesome new IThreadScopeInfo implementation within web.config:

<activerecord
   isWeb="true"
   threadinfotype="WcfThreadScopeInfo, Website">

Or if you’re using an InPlaceConfigurationSource and initializing ActiveRecord in your global.asax, you can use:

InPlaceConfigurationSource source = new InPlaceConfigurationSource();
// Stuff
source.ThreadScopeInfoImplementation = typeof(WcfThreadScopeInfo);

This will tell ActiveRecord to use WcfThreadScopeInfo to resolve the current session scope any time it needs to know.

In Summary…

Ok so what’s going on now? When a WCF request comes in, the IDispatchMessageInspector message inspector is run, which creates a new ActiveRecord scope for that request. The constructor for SessionScope will see if there’s already a SessionScope stack to add itself to, and it will do so by looking at the configured IThreadScopeInfo implementation. Our implementation will be able to use a ASP.NET context, a WCF context, or a thread context to store this stack in. This means that any time we need to look up the current session, which is done all over the place in the ActiveRecord framework, we’ll be able to find the current session scope or create one if necessary. When the request is ended, the session scope is disposed of and pending updates are committed to the database. If you had new’ed up any child session scopes on the stack, you’d of course be responsible for disposing of those properly as well.

Well, there you have it. Making WCF and ActiveRecord best friends is fairly straight forward, and lets you learn about the inner workings of NHibernate, ActiveRecord, and the WCF pipeline all at the same time. Have fun!

Advertisement

From → Technical

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: