One DbContext per request in ASP.NET MVC (without IOC container)

25,431

Solution 1

I would use the BeginRequest/EndRequest method, this helps ensure that your context is disposed of properly when the request is over with.

protected virtual void Application_BeginRequest()
{
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}

protected virtual void Application_EndRequest()
{
    var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
    if (entityContext != null)
        entityContext.Dispose();
}

And in your EntityContext class...

public class EntityContext
{
    public static EntityContext Current
    {
        get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
    }
}

Solution 2

I know this is not a recent question, but I'll post my answer anyway, because I believe someone may find it useful.

As probably many others, I followed the steps mentioned in the accepted answer. Yay, it works. HOWEVER, there's one catch:

Methods BeginRequest() and EndRequest() fire each time a request is made, but not only for aspx pages, but for ALL STATIC CONTENT! That said, if you use the code mentioned above and you have on your page let's say 30 images, you're re-instantiating your dbcontext 30 times!

The solution for this is to use a wrapping class for retrieving the context, something like this:

internal static class ContextPerRequest
{
      internal static DB1Entities Current
      {
          get
          {
              if (!HttpContext.Current.Items.Contains("myContext"))
              {
                  HttpContext.Current.Items.Add("myContext", new DB1Entities());
              }
              return HttpContext.Current.Items["myContext"] as DB1Entities;
          }
      }
 }

And then for disposing

protected void Application_EndRequest(object sender, EventArgs e)
{
   var entityContext = HttpContext.Current.Items["myContext"] as DB1Entities;
   if (entityContext != null) 
      entityContext.Dispose();
}

This modification ensures that you instantiate and dispose your context only once per request and only when needed. Selected answer instantiates context every single time.

Note: DB1Entities is derived from DbContext (generated by VS). You would probably want to alter it with your context name ;)

Note 2: in this example I'm working with just one dbcontext. If you need to work with multiple, you would need to modify this code according to your needs. Don't take this as some ultimate solution to world problems, because it certainly isn't a final product. It is meant just to give a hint, how it may be achieved in a very easy way.

Note 3: Same approach can be used in different situations as well, for instance when you'd like to share an instance of SqlConnection or any other... This solution isn't exclusive to DbContext object, nor to Entity framework.

Solution 3

One way would be to subscribe for the Application_BeginRequest event, inject the DbContext into the current HttpContext and in the Application_EndRequest fetch from the HttpContext and dispose. Anything in between (which is pretty much everything :-)) could fetch the DbContext from the current HttpContext and use it. And, yes, you should dispose it. And by the way is there any reason you don't use a DI framework which already does this for you among other useful things?

Solution 4

Small addition for Chad Moran answer. It is inspired by walther notes. To avoid context initialization for static content we should check current route handler (this example only for MVC):

protected virtual void Application_BeginRequest()
{
  var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(this.Context));
  if (routeData != null && routeData.RouteHandler is MvcRouteHandler)
  {
    HttpContext.Current.Items["_EntityContext"] = new EntityContext();
  }
}

Solution 5

If you implement IDisposable in your controller, and dispose context in disposing method, and instantiate new context in controller constructor, you should be safe as controller is instantiated for each request. I don't see, however, why would you want to do that? ... You should use DI, or make a context factory with one static instance of context. If you don't use one instance (you make one for each request) you are to have problems at some point. The problem with undisposed context is that EF caches data in context, and if some other instance of context changes something in DB that is already cached in another context - you have unconsistent state. Before DI became so popular, I used to have one static instance of context somewhere in application, and that is a lot faster and safer than having each request make its own context, but you need to implement state-checking code which makes sure that context connection to db is ok... There are a lot better solutions to this problem, and the best is to use some DI framework. I would recommend Ninject in combination with MVCTurbine, it is easy to set up and you can add it through NuGet.

Share:
25,431
devuxer
Author by

devuxer

Updated on December 24, 2020

Comments

  • devuxer
    devuxer over 3 years

    Apologies if this has already been answered, but how do you guarantee one Entity Framework DbContext per request if you are not using an IOC container? (The answers I've come across so far deal with IOC container solutions.)

    It seems like most solutions hook into the HttpContext.Current.Items dictionary, but how do you guarantee disposal of the DbContext when the request is finished? (Or is disposal not absolutely necessary with an EF DbContext?)

    Edit

    I'm currently instantiating and disposing my DbContext in my controllers, but I also have several separate instantiations of my DbContext in ActionFilters and my MembershipProvider (and I just noticed, also a couple validators). So, I thought it might be a good idea to centralize instantiation and storage of my DbContext to reduce overhead.

  • devuxer
    devuxer almost 13 years
    +1 Thanks for your answer. This makes sense. I'm not (currently) using an IOC container because the app is relatively simple, and I haven't yet come across any dependency injection scenarios that are remotely complex. I pretty much just inject my DbContext into various Services. But I'm not against using an IOC container.
  • RPM1984
    RPM1984 almost 13 years
    Doesn't matter if your app is simple. It's futureproofing. Set it up now while it's simple, then it's be easier when you need it as your app get's bigger. Unless of course your app will stay small.
  • devuxer
    devuxer almost 13 years
    @RPM1984, I see your point...the project will likely not develop any more layers of depth, but will expand in breadth. In other words, I expect to be adding more pages, but I basically have models, services, controllers, views, and validators, and I don't expect that to change. Right now, it's very easy to hook everything up (much of it is automated by MVC, in fact). I'm having trouble seeing how an IOC container will make life easier for me, but I'm interested in hearing what your strategy is.
  • RPM1984
    RPM1984 almost 13 years
    Well to me it's simple. You should always use interface-driven programming, at the very least for unit testing individual components. Then once your working with interfaces, create a basic DI registry to set up the dependencies. < 1 hours worth of work, makes your code clean and testable. Are you injecting concrete services into your controllers? If so, i would be interested to see how you are unit testing your controllers.
  • devuxer
    devuxer almost 13 years
    I'm not following your point. How does using a DI framework protect against inconsistent state? If two users attempt to edit the same record at the same time, I don't see how sharing a single context or using two separate contexts will make any difference. One of the users (the one that submits first) is going to experience unintended results.
  • devuxer
    devuxer almost 13 years
    mymex, inconsistent state is definitely a concern, but I don't see what difference it makes whether users share a single data context or use separate contexts. As I said to obrad, if two users are editing the same record at the same time, the user who submits first is going to have his changes written over by the user who submits second.
  • devuxer
    devuxer almost 13 years
    Example: let's say I decided to edit your answer. I click the "edit" button, and now I'm looking at an edit box with your answer in it. I change a few words. Before I hit submit, though, you decide to edit your answer. Now you are looking at an edit box, but the answer you see doesn't have any of the word changes I've made. Now I submit my answer. You make a few changes of your own, and you submit your answer. The result? Inconsistent state. Even if we were sharing a data context, it wouldn't have mattered, because the data context doesn't know a change has been made until the user submits.
  • devuxer
    devuxer almost 13 years
    mymex, please also see my comments on obrad's answer.
  • devuxer
    devuxer almost 13 years
    Example (continued): Thankfully, StackOverflow saves the entire edit history, so even though my changes got written over by yours, I could still go back and see what I wrote.
  • devuxer
    devuxer almost 13 years
    @RPM1984, I think you've hit on the issue...it has never occurred to me to unit test my controllers. They just return views. What would I test for?
  • RPM1984
    RPM1984 almost 13 years
    @DanM - really, that's it? No talking to the model, no viewmodel setup, no logic whatsoever? I find that hard to believe. If that's the case, then no unit testing is required.
  • devuxer
    devuxer almost 13 years
    @RPM1984, well, yes, I was exaggerating a bit. But for the most part, the logic is var model = _service.GetEditorModel(id); return View(model) or if (!ModelState.IsValid) return View(model); _service.Edit(model); return RedirectToAction("Index");. And I use a base class so that the concrete classes are usually extremely simple. I do have constructor overloads so my controllers could be unit tested, but I haven't actually written any tests.
  • RPM1984
    RPM1984 almost 13 years
    @DanM - okay, well that's your preference. But personally i would (and have) tests like: ensuring that for a valid model in the POST redirect result is returned (e.g testing PRG), ensuring that for an invalid model in the POST that view result is returned and the model state contains the error, etc. And for those tests, _service should be declared as IService and implemented as ITestService in your test project, to enable easy testing. Anyway, rant over - it's up to you. :)
  • devuxer
    devuxer almost 13 years
    @RPM1984, I realize I'm not entirely following best practices here. Unit testing in general is something I haven't quite gotten my head around given that I pretty much always work on small, one-person, non-life-or-death projects. I keep building projects with testing in mind (e.g., I almost always inject dependencies rather than "new things up", and I use patterns such as MVC & MVVM that are intended for testing), but then I never end up writing many tests. I feel like I should be doing it, but I almost every bug I encounter appears to be something only integration testing would catch.
  • devuxer
    devuxer almost 13 years
    @RPM1984, please feel free to sell me on it, though :) I've read about all the benefits of TDD and unit testing many times, but it's hard to want to do it when I haven't had failures by not doing it.
  • Goran Obradovic
    Goran Obradovic almost 13 years
    Yes, that is not easy solvable, but if you have need for having your system handle that kind of scenario, you must handle it at database level - add versioning to your records in database. When you try to create new version, you will encounter the one made by other user, and then you can ask user to decide to ignore/overwrite etc.
  • Goran Obradovic
    Goran Obradovic almost 13 years
    My point was when you have user editing complex object, and you show him some dropdowns so he/her can choose values on form. If administrator changes codebooks (adds/removes values in dropdowns) after user's dbcontext has already read that table, it wont be reflected in existing contexts - that is why you need to make sure that new context is constructed for every request if you want to have current data. Downside is performance - values are read from database every time. You need to decide which tradeoff to make - if you don't want to think about transactions and concurency that...
  • Goran Obradovic
    Goran Obradovic almost 13 years
    ... might happen when you add your object to context, but plan to edit it and save it later, then static context can cause some other user performing action to save changes to database - unintended behavior. But if you need performance, one context is way to go. Even better, you could make one static instance for all codebooks etc - you need key/value pairs anyway, and per-instance contexts for transactional actions. There are hundreds of possibilities. DI is only a tool that can help you do your job faster and easier, YOU are the one who solves (or creates) problems :)
  • devuxer
    devuxer almost 13 years
    obrad, It sounds like you're referring to the case where a table is used very frequently in dropdowns, and the goal is to avoid requesting this from the database over and over again unless it changes. I could definitely see wanting to cache the data in that case, but this isn't really a common scenario for me right now, and my app has a restricted user population, so it's not really critical that I minimize hits to the database. I am concerned about data integrity, however. I deal with that by restricting who can delete records and under what conditions.
  • Goran Obradovic
    Goran Obradovic almost 13 years
    If integrity is your primary concern, I would advice that you think about database records versioning: no update; for every change, new record with increased version number is created. It is a pain to implement (you need another "id" field, or primary key must be combination of id and version, and you need to implement logic for all this), but with that approach you will never have scenario in which user tries and succeeds to update record which was already updated by someone in the midtime. You will always be able to detect a newer version than the one being edited an to warn user about it.
  • BjarkeCK
    BjarkeCK over 11 years
    This is great, put put the static Current method inside the Context class, and this is perfect for me :)
  • Santosh
    Santosh over 10 years
    When I declare a private instance inside a controller (private EntityContext _db = EntityContext.Current;) and use it across all actions, I observe memory leak. Any idea why?
  • MattheW
    MattheW over 10 years
    I'd like to use that solution, but I've got another project in solution that handles DB access, so I cannot use HttpContext. Is there any way I can implement your advice?
  • walther
    walther over 10 years
    @MattheW, if you can somehow expose your db managing class to the UI, so you can dispose of it at the end of the request, then yes. This whole question is about creating a single context and sharing it until the request ends, so you don't recreate it over and over during a single user request. You just don't care about it - you instantiate it on demand and dispose of it when it's done and you send the results to the user.
  • MoXplod
    MoXplod over 10 years
    I have tried this, but it seems like if there are additional calls required from the razor view, then we get an exception that the objectcontext is exposed and we are trying to access the DB. Looks like EndRequest is called before razor view has finished rendering? What might i be doing wrong?
  • 99823
    99823 about 10 years
    Hi Walther - i'm confused I've been reading that storing your context in a static property can generate some nasty errors - is this the case? Ref: stackoverflow.com/questions/4847892/…
  • walther
    walther about 10 years
    @Loren, no, because in my solution the context is stored in the HttpContext.Current.Items collection and is disposed during the request end event. The static property only exposes this context from the collection and instantiates it if it doesn't exist yet. It is never stored in a static variable and is current context specific, so you can't run into problems like you've mentioned. You can as well transform the property into a method and everything will function exactly the same. I just like the syntax of properties more, so I've used it instead a method.
  • Jeff Camera
    Jeff Camera over 9 years
    Thank you. I prefer this solution over the global static method. In addition, if you instead hook Application_PreRequestHandlerExecute you can just check this.Context.Handler is MvcHandler since it will be set at this point in the processing pipeline.
  • Jone Polvora
    Jone Polvora over 6 years
    I probably would use Lazy<T> , it's perfect for this scenario, when an instance would be created only when really needed. Besides this, instantiating a DbContext is very cheep (expensive only the first time).