Caffeine-Powered Life

Accessing Application State Information

In every application I’ve every written, I’ve had to deal with state values in some way. At the simplest level, you’ll usually need to know the answer to three questions.

  1. Is there currently an authenticated user?
  2. Who is the current user?
  3. What is the current user allowed to do?

Just about every web application needs to solve these three problems. In fact, this very question was posed by a comment from my previous post. In the commenter’s example, he has a separation of layers that will not allow him to access things like HTTP context or session from his data tier library. (There’s no reference to System.Web from the data tier.)

You know what I’m thinking? This sounds like an interface!

public interface IUserState

{

  bool IsAuthenticated { get; }

  string UserName { get; }

  string[] Roles { get; }

}

This is a bit of a trick in a web environment, because the web is naturally stateless. You can’t use statics the same as you would with a desktop application. You must use a factory, similar to how HttpContext.Current is resolved.

I should point out that the concept of a user state is even lower than your data tier.

public class UserState

{

  private static IUserStateFactory factory;



  public static IUserState Current

  {

    get { return factory.UserState; }

  }



  public static void SetFactory(IUserStateFactory userStateFactory)

  {

    factory = userStateFactory;

  }

}

This part is important: we cannot store the user state instance in a static field. This is one of the biggest sources of problems that I find when working with ASP.NET application. (I’ve seen this all too often in both WebForms and MVC. Remember, the underlying ASP.NET technology is the same.) The static field life cycle is stored within the AppDomain. In an ASP.NET project, this AppDomain is not guaranteed to go to the same user every time. When you have multiple users on a web page. However, we can easily save the factory in a field, since the role of a factory is to create the objects.

Now, we need some implementations that will work with an HTTP session.

public class HttpSessionUserState : IUserState

{

  private readonly HttpSessionState session;



  public HttpSessionUserState(HttpSessionState session)

  {

    this.session = session;

  }



  public bool IsAuthenticated

  {

    get { return UserName != null; }

  }



  public string UserName

  {

    get

    {

      object userName = session["UserName"];

      return (userName != null) ? (string)userName : null;

    }

  }



  public string[] Roles

  {

    get

    {

      object roles = session["Roles"];

      return (roles != null) ? (string[])roles : new string[] { };

    }

  }

}

Yes, I prefer to return an empty array of roles, rather than null, in this case. The way .NET deals with nulls is a constant source of frustration, and I hate seeing NullReferenceException’s sprinkled through the error logs.

We also need an implementation of the factory.

public class HttpSessionUserStateFactory : IUserStateFactory

{

  public IUserState UserState

  {

    get

    {

      var session = HttpContext.Current.Session;

      return new HttpSessionUserState(session);                

    }

  }

}

To round out this example we need to register the factory with the static class. This should be done as part of your application startup. In a web app, that means you should put this in your Global.asax file. Create a method like this, and add a call to this method to your Application_Start.

private void RegisterUserStateFactory()

{

  var userStateFactory = new HttpSessionUserStateFactory();

  UserState.SetFactory(userStateFactory);

}

This is a little lengthy. We’re looking at 5 total classes, but all of the classes are quite small. The first three classes are in a base layer. The specific implementations are in your are part of your startup layer. This works because we are depending on an interface, not a specific implementation. Unit testing is quite easy as well. We can create InMemoryUserState and InMemoryUserStateFactories quite easily, or we can just mock out their implementations.

Back to my previous post

Updating modified entries now needs to look something like this.

private void UpdateModifiedEntries()

{

  var modifiedEntries = ChangeTrackerEntries.Where(e => e.State == EntityState.Modified && e.Entity is IAuditable).Select(e => e.Entity);

  foreach (var modifiedEntry in modifiedEntries)

  {

    modifiedEntry.UpdatedAt = DateTime.UtcNow;

    modifiedEntry.UpdatedBy = UserState.Current.UserName; // used to be HttpContext.Current.UserName

  }

}

Happy to help!

Comments