# Friday, May 21, 2004

I was just reading Steven Padfield's article on unit testing ASP.NET code by creating your own HttpContext outside of IIS (which is a very useful technique) and it got me thinking about a technique that I've gotten a fair amount of mileage out of lately, namely creating my own context object that will be available anywhere in a call stack, just like the HttpContext is.

When I started looking into how to implement such a think, I was thinking in terms of deriving from ContextBoundObject, which seemed like overkill.  So I fired up the ever-handy Reflector and found out how HttpContext handles itself.  Turns out that no ContextBoundObject is needed.  Hidden in the bowls of System.Runtime.Remoting.Messaging is a method called CallContext.SetData(string, object) that will stick a named object value into your call context, which can be retrieved from anyplace on the current call stack.  Pretty handy.  If you wrap that in an object like HttpContext, you can store your own context values, and potentially provide context-sensitive methods such as HttpContext.GetConfig().

What you end up with is an object that looks something like this:

using System;
using System.Collections;
using System.Runtime.Remoting.Messaging;

namespace MyContext
{
public class ContextObject
{
private const string ContextTag = "MyContextObject";

private ContextObject()
{
}

/// <summary>
/// returns a valid context object, creating one if
/// none exists
/// </summary>
public static ContextObject CurrentContext
{
get
{
object o = CallContext.GetData(ContextTag);
if(o == null)
{
o = new ContextObject();
CallContext.SetData(ContextTag,o);
}

if(!( o is ContextObject))
{
throw new ApplicationException("Corrupt ContextObject");
}

return (ContextObject)o;
}
}

/// <summary>
/// Clears out the current context. May be useful
/// in situations where you don't have complete
/// control over your call stack, i.e. you aren't at the top of
/// the application call stack and need to maintain
/// a separate context per call.
/// </summary>
public static void TeardownCurrentContext()
{
CallContext.FreeNamedDataSlot(ContextTag);
}

private string contextValue1;

///<summary>
/// a sample value to store to/pull from context
///</summar>
public string ContextValue1
{
get
{
return contextValue1;
}
set
{
contextValue1 = value;
}
}
}
}

You can use the context object from anywhere in your call stack, like this

public class Tester
{
public static void Main(string[] args)
{
ContextObject co = ContextObject.CurrentContext;
co.ContextValue1 = "Hello World";
OtherMethod();
}

public static void OtherMethod()
{
ContextObject co = ContextObject.CurrentContext;
Console.WriteLine(co.ContextValue1);
}
}


 

The resulting output is, of course, "Hello World", since the context object retains its state across calls.  This is a trivial example, and you wouldn't really do it this way, but you get the idea.