In a lot of articles showing you how to get started with EntityFramework they have a good basis if you're using something like a desktop application where you can easily share the context object around your different classes. In the world of ASP.NET though things become a little more confusing.

Let's Say You Have a Website

So you make your shiny new ASP.NET MVC5 website along with a few controllers. Most examples will show you to do something like this:

public async Task<ActionResult> Page(string pageId)
{
    using(var db = new MyContext())
    {
        var page = await db.Pages.FirstOrDefaultAsync(m => m.PageID == pageId);
        return View(page);
    }
}

However there is a killer that you won't hit until trying to performance tune a large website. Every time you call new MyContext() EF initializes. Once during a request is fine, but if you call new more then once during a single request (through partial views and the like) you're wasting resources and will quickly take a performance hit.

You can easily see this hit using a tool such as Hibernating Rhinos' EF Profiler (which I suggest you do use when tuning).

With an EF profiler you can trace how many active connections and EF contexts you have generated in an application. EF will automatically handle the connection pooling for you and isn't something you really need to be concerned about unless you're at a massive scale. However context generation should be controlled by you.

Using a Base Controller

My go-to method for creating and handling a shared context while staying DRY is to use a base controller that my other controllers will inherit from.

Set Up

// must be marked abstract!
public abstract class BaseController : Controller
{
    // Context Variables
    private MyContext _siteContext;

    // Using this isn't strictly necessary, but I'm the paranoid type.
    protected MyContext SiteContext
    {
        get { return _siteContext; } 
    }

    // Initialize is called every time a new controller is instantiated, even if it inherits from BaseController
    protected override void Initialize(RequestContext requestContext)
    {
        //requestContext contains a dictionary that is used throughout the life of a single request. 
        //Using this we can store and retrieve expensive objects such as the EF context.
        // Optionally you can disable change tracking to lower the overhead of EF if all you are doing is loading data.
        if(requestContext.HttpContext.Items.Contains("_siteContext"))
        {
            _siteContext = (MyContext)requestContext.HttpContext.Items["_siteContext"];
        }
        else // Didn't exist so it's the first time in so make the context.
        {
            _siteContext = new MyContext();
            requestContext.HttpContext.Items["_siteContext"] = _siteContext;
        }
    }
}

And then use this to inherit your controllers from and do your EF calls.

public class HomeController : BaseController
{ 
    public async Task<ActionResult> Page(string pageId)
    {
        var page = await SiteContext.Pages.FirstOrDefaultAsync(m => m.PageID == pageId);
        return View(page);
    }
}

Clean Up

While you can override the Dispose method on the BaseController you don't want to dispose the context at that time otherwise you're going to have some issues because it will be called every time a controller has completed its action for a single request which means that if you have a page that uses multiple controllers Dispose will be called before the request is completed.

The work around is to override the Application_EndRequest in Global.asax.

protected void Application_EndRequest()
{
    if(Request.RequestContext.HttpContext.Items.Contains("_siteContext"))
    {
        var context = (MyContext)Request.RequestContext.HttpContext.Items["_siteContext"];
        context.Dispose();
    }
}

What Never To Do

I once learned a hard lesson while trying to solve my issue of generating a very complex context with every controller. I made a static class. Never. EVER! EVER EVER! DO THAT!

What is so very deceptive about it is that with a single user it works. It works flawlessly. It works so well you'll wonder why you never did it before. When you have even 10 users all doing CRUD operations you're going to have a huge problem.

What the Context Does

To understand the issue requires a little knowledge of what the EF context does. In short, and the part that matters for this, is that it keeps a state of all of the changes that have occurred during the life span of the context. This is the reason that this call will always fail:

 MyContext.Entry(someEntity).State = EntityState.Modified;
 MyContext.Entry(someEntity).State = EntityState.Modified;

A context can (and will throw an exception if you don't abide by this) only track a single entity's state once. You can work around this by explicitly removing the entity from being tracked, but if you're hitting this and not expecting it you may be surprised. Which is exactly what will happen when you use a static class to wrap a context.

When you mark the class as static it makes it persist across requests, all the way until the application is recycled by IIS. Because of this a context will continue to track all of the changes that have been given to it. So what happens when two users update the same record? It explodes in exactly the same way the example above explodes. This is why a context should be unique to a request. The only way for a single request to try to add the same entity to the context twice is to explicitly try to do that, which will instantly show up when you test. Sharing the context between requests basically creates a race condition, in that the first time it will alwayswork. After that it will always fail.

About Author

Siva Katir

Siva Katir

Senior Software Engineer working at PlayFab in Seattle Washington.