Abstracting Request State
Posted by Davy Brion on January 17th, 2009
Sometimes it’s useful to be able to store an object somewhere so you can easily access it for the duration of the current request, instead of having to pass it around with every method call that you make. That request could be an ASP.NET request, or a request in your WCF service layer. I used to resort to storing these objects in ThreadStatic fields (which is basically a static reference for each thread), thinking that it would be safe because only one thread handles a complete request. Last week i read that some requests can be paused and resumed by another thread. If you’re using ThreadStatic fields, this could lead so major issues which would be a royal pain in the ass to debug. In order to prevent this possible problem, i wanted to have a safe way to keep state that should be available for the duration of a single request.
If your code executes in an ASP.NET environment, you can safely use the HttpContext.Current.Items dictionary for this. If your code executes in a WCF environment, you can store these things in the OperationContext. I don’t want my code to be tightly coupled to either ASP.NET or WCF, so i wanted some kind of abstraction. This is the approach that i came up with.
First, we have the IRequestState interface:
public interface IRequestState
{
T Get<T>(string key);
void Store(string key, object something);
}
This just offers a way to store objects and retrieve them. That’s pretty much al we need, right?
Then we have the ASP.NET implementation:
public class AspNetRequestState : IRequestState
{
public T Get<T>(string key)
{
return (T)HttpContext.Current.Items[key];
}
public void Store(string key, object something)
{
HttpContext.Current.Items[key] = something;
}
}
Very simple stuff… the AspNetRequestState implementation simply uses the HttpContext.Current.Items dictionary underneith to store and retrieve the objects.
For WCF, it is slightly more complicated. Every WCF call is an operation and it has a context as well, which is provided through the OperationContext class. The OperationContext class doesn’t have an Items dictionary like HttpContext does, but it does have a way to add extensions to the context. We can use this extensions mechanism to store state which should be kept around for the duration of the current WCF operation. First, we need to define our Extension:
public class MyExtension : IExtension<OperationContext>
{
public MyExtension()
{
State = new Dictionary<string, object>();
}
public IDictionary<string, object> State { get; private set; }
// we don't really need implementations for these methods in this case
public void Attach(OperationContext owner) { }
public void Detach(OperationContext owner) { }
}
The IExtension interface that we must implement defines the Attach and Detach methods but we don’t really need them for what we’re trying to do. This extension simply initializes a Dictionary instance and exposes it with a public getter. Now we can easily create our WcfRequestState implementation:
public class WcfRequestState : IRequestState
{
private static IDictionary<string, object> State
{
get
{
var extension = OperationContext.Current.Extensions.Find<StateExtension>();
if (extension == null)
{
extension = new StateExtension();
OperationContext.Current.Extensions.Add(extension);
}
return extension.State;
}
}
public T Get<T>(string key)
{
if (State.ContainsKey(key))
{
return (T)State[key];
}
return default(T);
}
public void Store(string key, object something)
{
State[key] = something;
}
}
Pretty simple as well, and pretty similar to the AspNetRequestState implementation. The AspNetRequestState implementation is able to simply use the HttpContext.Current.Items dictionary, which we can’t use here. So when we want to access the ‘State’ dictionary in this implementation, we look it up in the current OperationContext’s Extensions collection. If it’s not there yet, we add a new instance of our MyExtension class to the OperationContext’s Extensions collection.
Now we can use this wherever we need to store something for the duration of the current request, regardless of whether we’re executing in an ASP.NET or WCF context. Just configure your IoC container to create instances of AspNetRequestState whenever an IRequestState instance is needed in your WebApplication, or configure it to return WcfRequestState instances in your WCF service. The code that needs to store some request state will no longer have to resort to using ThreadStatic fields, and it doesn’t need to know about it’s runtime environment either. It merely needs an instance of IRequestState.
January 18th, 2009 at 12:25 pm
This comes up every so often (ASP.Net thread agility).
Ayende has a class (as you’d expect!!) called LocalData in Rhino.Commons that does something similar (but doesn’t cover the WCF scenario). I and many others have our own various solutions.
There are 2 questions that I always have with this:
1. if this is such a problem in ASP. Why doesn’t the ASP runtime do that magic to migrate the thread statics for us? It must do something similar to make HttpContext.Current migrate.
2. How does TransactionScope work? Internally it seems to use a ThreadStatic in a helper class called ContextData.
The answer to 1. is… That’s just the way it is, we’d best just get on with it!
The answer to 2. is unclear. Does this also fail in the high load ASP scenario or is there some other magic I’m not seeing that we can borrow instead of many different wheels?
–Andy
January 18th, 2009 at 12:29 pm
if they were to migrate the ThreadStatic values for us, it would appear to work correctly but it would be a pretty ugly hack where the whole concept of ThreadStatic is pretty much violated
dunno about TransactionScope though… haven’t used that in a while
January 18th, 2009 at 6:12 pm
violated: Sure, as far as the clr runtime spec is concerned. But then ThreadStatic is, as you’ve discovered, pretty broken in asp anyway. (unless you really know where asp async method may not complete synchronously).
TransactionScope: I assume from your comment that you use some ORM (NHibernate etc). Have you no need for ambient transaction/session to be able to compose various actions coded in separate methods/classes?
January 18th, 2009 at 6:21 pm
i indeed use NHibernate
I basically start a transaction whenever i enter my service layer, and everything that happens from then on is managed by the parent transaction. I can call any piece of code knowing that everything that gets executed can be committed or rolled back by the parent transaction. I use a custom Unit Of Work implementation for this.
January 19th, 2009 at 10:14 am
And that uses the same technique to ensure you always get the correct ambient transaction?
I assume that your custom UoW is similar to others out there? Giving nestable transaction scope, rollback, commit etc.
You intimate that TransactionScope isn’t useful. What, essentially, do you see as the benefit of a custom UoW over TransactionScope?
January 19th, 2009 at 10:50 am
the UoW implementation is probably very similar to other ones
i didn’t really say that TransactionScope isn’t useful, just that i don’t use it
the only reason why i don’t use it is because i just stick with the transactions that NHibernate provides me with, and that works well for me
January 19th, 2009 at 12:20 pm
Hello Davy,
So your custom UoW starts NH transaction when you enter your service layer … when you say ’service layer’, are you talking about your WCF services, or another logical service layer below your WCF services ? if WCF service layer, why not use the transactional options of WCF over your custom implementation?
What I’m usually doing is have an interceptor on my Business Layer to start transactions that nest with the ones from the (WCF) service layer (if any) … that’s where System.Transactions TransactionScope come in handy … but that implies that the underlying ORM used must support TransactionScope.
January 19th, 2009 at 12:26 pm
yes, i’m talking about a WCF service layer
i guess i could use WCF’s transactional options, but so far this custom UoW approach is working pretty good so i’m not just going to change it.
btw, NHibernate does have support for TransactionScope (although that might just be in the current trunk, not sure about that) so what you’re doing should work as well
January 19th, 2009 at 12:33 pm
It works, I’ve tested in the past (if my memory is not failing me lol) … not sure if ISession is fully memory-transactional yet though (but that is rarely usefull anyway, you’re usually supposed to throw away your ISession instance in case of a Tx Rollback).
January 19th, 2009 at 12:35 pm
ah ok, i also thought it worked but i know that Ayende recently did some work on TransactionScope in the trunk so i wasn’t sure… i think those changes were mostly MSDTC related though
January 19th, 2009 at 1:26 pm
[...] Abstracting Request State (Davy Brion) [...]
March 17th, 2009 at 11:01 pm
Davy –
Just catching up on your post. Does this tie into your unit of work article? Would the IRequestState be used in the ActiveSessionManager class to replace the field called “current?”
I was thinking that for testing, I would have an Implementation of IRequestState that did not depend on HttpContext. In order to keep with the IRequestState interface, it seems like I would need to use something like a Dictionary. Does that sound right?
Good stuff. Learning lots.
C
March 17th, 2009 at 11:07 pm
“Would the IRequestState be used in the ActiveSessionManager class to replace the field called “current?” ”
yup
and yeah, you could definitely use a Dictionary-based TestRequestState implementation for your tests