Managing Entity Framework DbContext in asp.net mvc
22 December 2013
You are usually advised to create dbContext for each method. Like this:
using (var db = new MyDbContext())
{
db.DoSomething();
db.SaveChanges();
}
In some examples you can probably see unique dbContext created in Controller constructor.
Both approaches are good, but after some extended googling and thinking I came up with the following as the best way to manage EF DbContext in asp.net mvc: Context Per Request. Here is the idea: for every request that is being made to asp.net mvc application, we will (lazily) create a DbContext, that will serve every method during this request being processed, and disposed as request is ended. Since each request is single threaded, we will not have any threading problems with this DbContext during the request.
Here is the implementation:
/// <summary>
/// Not thread safe.
/// </summary>
public static class ContextPerRequest
{
private const string myDbPerRequestContext = "MDPRC";
public static MyDbContext Db
{
get
{
if (!HttpContext.Current.Items.Contains(myDbPerRequestContext))
{
HttpContext.Current.Items.Add(myDbPerRequestContext, new MyDbContext());
}
return HttpContext.Current.Items[myDbPerRequestContext] as MyDbContext;
}
}
/// <summary>
/// Called automatically on Application_EndRequest()
/// </summary>
public static void DisposeDbContextPerRequest()
{
// Getting dbContext directly to avoid creating it in case it was not already created.
var entityContext = HttpContext.Current.Items[myDbPerRequestContext] as MyDbContext;
if (entityContext != null)
{
entityContext.Dispose();
HttpContext.Current.Items.Remove(myDbPerRequestContext);
}
}
}
Following code is in Global.asax.cs:
protected void Application_EndRequest()
{
ContextPerRequest.DisposeDbContextPerRequest();
}
Sample usage:
public static int AddQuote(string fact, string fileName)
{
var quote = new Quote
{
CreatedDate = DateTime.Now,
IsApproved = false,
Text = fact,
ImageFilename = fileName
};
ContextPerRequest.Db.Quotes.Add(quote);
ContextPerRequest.Db.SaveChanges();
return quote.Id;
}
This one may as well be not static.
So, what are benefits of this approach:
- Cleaner code. You don't have to wrap everything in using(){}
- Less space for an error. This way you can't forget to dispose your dbContext. It is always automatically disposed for you.
- During each request, you are automatically having benefits of EF caching that is being made as you query DB. If one method need to query all Quotes, you don't have to worry about querying it in the next method: data will be taken instantly from cache. In rare cases you don't want to use cache between your method calls, you can state it explicitly.
What are disadvantages? As far as I know, there are none. Well, there are two cases I can imagine where such approach would not work:
- If you are using multiple threads during a single Request. You will obviously have to have separate DbContext per each thread.
- If there's just no HttpContext. I have met such problem when I was working with early versions of SignalR. There were just no HttpContext.Current! You will have to change example code accordingly when HttpContext is not available.
Here you go, have fun using this simple but powerful code. You are more than welcome to share ideas on what can be done better here. And, most important, if you think you can see some problem with current approach — please share your thoughts.