I some times like to give a deeper dive into general practices. Today is one of those days. Context and settings are somethings you have to deal with in every project. We are using default patterns for this. The beauty of Episerver is that you are not stick to weird hacks, you just can implement things as you are used to do (in "normal" .net). The context / state of an app is in our situation usually handled in the IAppContext. A wrapper (and single point of implementation) for all context related things.
Building a wrapper
The AppContext (in our situation) is basically a wrapper around context based information like cookie and session data, culture and other things. However the context can be used anywhere in you application. One of the main advantages is that it can be easily mocked and another is that if you like to change behavior you can change it there. It’s a single point of implementation for everything context related.
The interface
The interface IAppContext is created in your core / domain project and look as something like this;
public interface IAppContext
{
string UserName { get; }
bool LoggedIn { get; }
}
The actual implementation
The implementation depends, but for a webproject this can be something like this;
public class WebContext : IAppContext
{
public static IAppContext Current => ServiceLocator.Current.GetInstance<IAppContext>();
protected readonly HttpContextWrapper HttpContext;
#region Constructor
public WebContext(HttpContextWrapper httpContext)
{
HttpContext = httpContext;
}
#endregion
#region IAppContext
public bool LoggedIn => HttpContext.User.Identity.IsAuthenticated;
public string UserName => HttpContext.User.Identity.Name;
#endregion
#region Protected methods
// if you need cookies for certain properties;
protected virtual HttpCookie ReadCookie()
{
return HttpContext.Request.Cookies["appcontext"] ?? new HttpCookie("appcontext");
}
protected virtual void WriteCookie(HttpCookie cookie)
{
cookie.Expires = DateTime.Now.AddDays(90); // sliding expiration
HttpContext.Response.Cookies.Add(cookie); // write cookie with new expiration to response stream
}
#endregion
}
The registration
An IAppContext is registered in the Structuremap Container. Make sure to scope it for the HttpContext. The lifetime of the object can never be longer than a single request.
container.Configure(x => x.For<IAppContext>().HttpContextScoped().Use(() => new WebContext(new HttpContextWrapper(HttpContext.Current))));
Using it & pitfalls
You are allowed to use the context anywhere. That’s the beauty of it. If you f.e. like to know in a service layer if a user is loggedIn, ask for it via the IAppContext (even if the web layer is a black box for that layer), or you like to know which user it is, or you like to know what the current market, current culture or anything else is. However there are some pitfalls.
Pitfall 1; Using it wrong in a singleton.
You always need to remember that the AppContext is contexted based. You can’t define a singleton which needs the "Current AppContext" in the constructor. The singleton is than created with the current state of that moment. Therefor it’s wise to make sure the AppContext is lazy loaded in these senarios. This can be done like this;
container.Configure(x => x.For<ISomeService>().Use(() => new SomeService(() => ServiceLocator.Current.GetInstance<IAppContext>())).Singleton();
Where SomeService is implemented like this
public class SomeService : ISomeService
{
public CurrentMarket(Func<IAppContext> appContext)
{
// ...
}
// ...
}
Pitfall 2; Using it for other settings
Another pitfall is that you are going to used it for setting related things (global or site), like "CurrentLogo", "Current Startpage" or something like that. These things are not context related. This is site or even instance related (in reading; the same for more users, more contexts etc.) Using the AppContext for these senarios can cause performance drops and more importantly the context does get to much responsibilities and making it less maintainable with other problems ahead.
Verdict
Using an AppContext is easy and the use of it is even easier. But use it with caution.