Developer's Perception

Odd thoughts from a developer.

Working Effectively With ASP.NET and HttpContext

| Comments

Often in an ASP.NET WebForms application you will get up with unmanageable depencencies on the ASP.NET web stack expressed through the HttpContext class.

This often appears in the form of code like this:

1
2
3
4
5
public void DoSomeCoreStuff()
{
  var userId = (string)HttpContext.Current.Session["userId"];
  DoSomeMoreCoreStuff(userId);
}

This code could be located anywhere in you codebase, and I have seen examples of such calls to session being done more or less inline in a string describing an SQL query. It does not necessarily have to be the Session property that is accessed though, the problem is the same if Application or other bits of the current http context is accessed this way.

There is a number of problems with this approach.

  • Using a string like this is not strongly typed, which easily leads to errors. For instance the session variables might have had a different casing like “CurrentUserId”.
  • A static reference to HttpContext.Current causes a number of problems. Firstly, it will be hard to use the code in question outside the ASP.NET stack, and secondly it will be equally hard to write meaningful tests for the code in questions.

Options

There are a number of ways to solve this problem leading to a much cleaner design enabling testing and reuse. All the options involves pushing the actual call to HttpContext.Current as far up in the call chain as possible, meaning in the code behind of the aspx page.

Simple Types

Given that the currentUserId in our example above is an string (or any other simple type or object that can be easily constructed) why not just use that? It can be injected through the constructor or the method in question.

1
2
3
4
public void DoSomeCoreStuff(string userId)
{
  DoSomeMoreCoreStuff(userId);
}

Domain Specific Wrapper Class

Often it is not as simple as a single entry that you want to retrieve from the session. Maybe there is a group of related entries that you want to use.

1
2
3
4
5
6
7
public void DoSomeCoreStuff()
{
  var userId = (string)HttpContext.Current.Session["currentUserId"];
  var userColor = (string)HttpContext.Current.Session["favoriteColor"];
  var userNickname = (string)HttpContext.Current.Session["nick"];
  DoSomeMoreCoreStuff(userId, userColor, userNickname);
}

In that case an interface named according to the relation (for instance UserContext in our example) and injected can solve this problem. The key is to have an implementation of the interface that actually wraps Session and ensure that the string key for the entry is not copied all over the code. The wrapper implementation is instantiated at the top level of the web stack, most often in the code behind class and elsewhere we code against the interface in a inversion of control style.

1
2
3
4
5
6
7
public void DoSomeCoreStuff(UserContext userContext)
{
  var userId = userContext.Id;
  var userColor = userContext.FavoriteColor;
  var userNickname = userContext.NickName;
  DoSomeMoreCoreStuff(userId, userColor, userNickname);
}

The implementation of the interface looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HttpContextUserContext : UserContext
{
  HttpContext context;

  public HttpContextUserContext(HttpContext context)
  {
      this.context = context;
  }

  public string Id
  {
      get
      {
          return (string)context.Session["currentUserId"];
      }
  }

  public string FavoriteColor
  {
      get
      {
          return (string)context.Session["favoriteColor"];
      }
  }

  public string NickName
  {
      get
      {
          return (string)context.Session["nick"];
      }
  }
}

It might seem like a lot of code, but the flexibility is definitely worth the small overhead of adding this.

HttpContextBase

The main problem with the original implementation of the HttpContext class is that it is not based on an interface nor an abstract base class. This means that reusing code that depends on HttpContext can be hard, and testing equally hard. Microsoft realized this problen when they released the MVC framework and added a class called HttpContextBase. The funny thing is that HttpContext does not inherit from HttpContextBase as that would be a breaking change, but it has the exact same API.

Thus, our problem from above with the static dependency could be solved as the following stepwise refactoring:

  • Ensure that HttpContext is injected through the constructor or the method in question and ensure that HttpContext.Current is passed in as that value.
  • Replace HttpContext in the constructor or method with HttpContextBase and replace HttpContext.Current with new HttpContextWrapper(HttpContext.Current)

And our example would then look something like this:

1
2
3
4
5
public void DoSomeCoreStuff(HttpContextBase context)
{
  var userId = (string)context.Session["userId"];
  DoSomeMoreCoreStuff(userId);
}

This might seem a bit overkill, but we are now able to use our method with a different implementation of HttpContextBase for reuse and testing purposes.

When doing a different implementation you should know that every base implementation in HttpContextBase just throws a NotImplementedException. Thus, you will need to override that different bits in your test and reuse implementations depending on what you really require. Alternatively, there are a couple of generic open source implementations available if you want to pull in that extra dependency.

In general I would recommend using one of the first approaches, but if you are working with a fairly large system that adheres to the big ball of mud “design” antipattern this might be difficult. Maybe it is just to difficult to discern any meaningful relation between the different data retrieved from HttpContext, and with this approach you can at least start writing some tests to get control back of the code.

As I mentioned earlier this is just one simple example as HttpContext have a lot of data and functionality built in.s

Comments