One DbContext per web request... why?
Asked Answered
I

9

444

I have been reading a lot of articles explaining how to set up Entity Framework's DbContext so that only one is created and used per HTTP web request using various DI frameworks.

Why is this a good idea in the first place? What advantages do you gain by using this approach? Are there certain situations where this would be a good idea? Are there things that you can do using this technique that you can't do when instantiating DbContexts per repository method call?

Icecap answered 14/5, 2012 at 14:37 Comment(2)
Gueddari in mehdi.me/ambient-dbcontext-in-ef6 calls DbContext instance per repository method call an antipattern. Quote: "By doing this, you're loosing pretty much every feature that Entity Framework provides via the DbContext, including its 1st-level cache, its identity map, its unit-of-work, and its change tracking and lazy-loading abilities." Excellent article with great suggestions for handling the lifecycle of DBContexts. Definitely worth reading.Worldwide
because the Unit of Work pattern, instantiate and dispose DbContext instance on each request, it was made to work this way, it's very performaticHereof
C
625

NOTE: This answer talks about the Entity Framework's DbContext, but it is applicable to any sort of Unit of Work implementation, such as LINQ to SQL's DataContext, and NHibernate's ISession.

Let start by echoing Ian: Having a single DbContext for the whole application is a Bad Idea. The only situation where this makes sense is when you have a single-threaded application and a database that is solely used by that single application instance. The DbContext is not thread-safe and since the DbContext caches data, it gets stale pretty soon. This will get you in all sorts of trouble when multiple users/applications work on that database simultaneously (which is very common of course). But I expect you already know that and just want to know why not to just inject a new instance (i.e. with a transient lifestyle) of the DbContext into anyone who needs it. (for more information about why a single DbContext -or even on context per thread- is bad, read this answer).

Let me start by saying that registering a DbContext as transient could work, but typically you want to have a single instance of such a unit of work within a certain scope. In a web application, it can be practical to define such a scope on the boundaries of a web request; thus a Per Web Request lifestyle. This allows you to let a whole set of objects operate within the same context. In other words, they operate within the same business transaction.

If you have no goal of having a set of operations operate inside the same context, in that case the transient lifestyle is fine, but there are a few things to watch:

  • Since every object gets its own instance, every class that changes the state of the system, needs to call _context.SaveChanges() (otherwise changes would get lost). This can complicate your code, and adds a second responsibility to the code (the responsibility of controlling the context), and is a violation of the Single Responsibility Principle.
  • You need to make sure that entities [loaded and saved by a DbContext] never leave the scope of such a class, because they can't be used in the context instance of another class. This can complicate your code enormously, because when you need those entities, you need to load them again by id, which could also cause performance problems.
  • Since DbContext implements IDisposable, you probably still want to Dispose all created instances. If you want to do this, you basically have two options. You need to dispose them in the same method right after calling context.SaveChanges(), but in that case the business logic takes ownership of an object it gets passed on from the outside. The second option is to Dispose all created instances on the boundary of the Http Request, but in that case you still need some sort of scoping to let the container know when those instances need to be Disposed.

Another option is to not inject a DbContext at all. Instead, you inject a DbContextFactory that is able to create a new instance (I used to use this approach in the past). This way the business logic controls the context explicitly. If might look like this:

public void SomeOperation()
{
    using (var context = this.contextFactory.CreateNew())
    {
        var entities = this.otherDependency.Operate(
            context, "some value");

        context.Entities.InsertOnSubmit(entities);

        context.SaveChanges();
    }
}

The plus side of this is that you manage the life of the DbContext explicitly and it is easy to set this up. It also allows you to use a single context in a certain scope, which has clear advantages, such as running code in a single business transaction, and being able to pass around entities, since they originate from the same DbContext.

The downside is that you will have to pass around the DbContext from method to method (which is termed Method Injection). Note that in a sense this solution is the same as the 'scoped' approach, but now the scope is controlled in the application code itself (and is possibly repeated many times). It is the application that is responsible for creating and disposing the unit of work. Since the DbContext is created after the dependency graph is constructed, Constructor Injection is out of the picture and you need to defer to Method Injection when you need to pass on the context from one class to the other.

Method Injection isn't that bad, but when the business logic gets more complex, and more classes get involved, you will have to pass it from method to method and class to class, which can complicate the code a lot (I've seen this in the past). For a simple application, this approach will do just fine though.

Because of the downsides, this factory approach has for bigger systems, another approach can be useful and that is the one where you let the container or the infrastructure code / Composition Root manage the unit of work. This is the style that your question is about.

By letting the container and/or the infrastructure handle this, your application code is not polluted by having to create, (optionally) commit and Dispose a UoW instance, which keeps the business logic simple and clean (just a Single Responsibility). There are some difficulties with this approach. For instance, where do you Commit and Dispose the instance?

Disposing a unit of work can be done at the end of the web request. Many people however, incorrectly assume that this is also the place to Commit the unit of work. However, at that point in the application, you simply can't determine for sure that the unit of work should actually be committed. e.g. If the business layer code threw an exception that was caught higher up the callstack, you definitely don't want to Commit.

The real solution is again to explicitly manage some sort of scope, but this time do it inside the Composition Root. Abstracting all business logic behind the command / handler pattern, you will be able to write a decorator that can be wrapped around each command handler that allows to do this. Example:

class TransactionalCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    readonly DbContext context;
    readonly ICommandHandler<TCommand> decorated;

    public TransactionCommandHandlerDecorator(
        DbContext context,
        ICommandHandler<TCommand> decorated)
    {
        this.context = context;
        this.decorated = decorated;
    }

    public void Handle(TCommand command)
    {
        this.decorated.Handle(command);

        context.SaveChanges();
    } 
}

This ensures that you only need to write this infrastructure code once. Any solid DI container allows you to configure such a decorator to be wrapped around all ICommandHandler<T> implementations in a consistent manner.

Cottage answered 14/5, 2012 at 17:59 Comment(17)
Wow - thanks for the thorough answer. If I could upvote twice, I would. Above, you say "...no intention of letting a whole set of operations operate inside the same context, in that case the transient lifestyle is fine...". What do you mean by "transient", specifically?Icecap
For the CommandHandler approach (which is something that I am actually using), does this mean that the DbContext will be shared across handlers? This also means that there is only one DbContext for one HTTP request?Icecap
@Andrew: 'Transient' is a Dependency Injection concept, which means that if a service is configured to be transient, a new instance of the service is created each time it is injected into a consumer.Cottage
@Andrew: It is up to you to architecturally decide whether it is okay for handler to execute other commands (and thus depend on other handlers). In that case multiple handlers in the same request should probably run in the same DbContext. Whether or not handlers depend on each other, it is quite natural to have handlers depend on other services that need to run in the same DbContext (such as query operations), but still you could have multiple DbContexts per web request (but probably still one single context per 'scope').Cottage
@Cottage Great stuff! One thing I don't understand. Would you have to write handlers from all CRUD operations? What if I wanted to query for customer, add or delete one? Would I have to implement ListCustomerCommandHandler(), DeleteCustomerCommandHandler() etc.? What if I had many entities? I tried posting this on your website but I kept getting redirected to some spam site (I filled out the capcha)Bargeman
@user981375: For CRUD operations you could create a generic CreateCommand<TEnity> and a generic CreateCommandHandler<TEntity> : ICommandHandler<CreateCommand<TEntity>> (and do the same for Update, and Delete, and had a single GetByIdQuery<TEntity> query). Still, you should ask yourself whether this model is a useful abstraction for CRUD operations, or whether it just adds complexity. Still, you might benefit from the possibility to easily add cross-cutting concerns (through decorators) using this model. You'll have to weigh the pros and cons.Cottage
@Cottage Pretty good answer. Could you define simple application (i have like 10 repositories can i consider this a simple application) ? to be honest i don't think this would be a problem to pass the context around methods.. this is simple as you stated. However, if you have a very complex application which use tons of repositories and transactions this might become a concern. But actually you only have to pass this to repositories constructors. So you end up having a contextfactory injected along with repositories which isn't bad at all. Seem clean to me.Chau
I can't define 'simple'. That's up to you, but an application with only 10 repositories can still be very complex (DDD based applications tend to have ja small amount of repositories for instance)Cottage
+1 Would you believe I wrote all of this answer before actually reading this ? BTW IMO I think its important for you to discuss the Disposal of the DbContext at the end (though its great that you're staying container agnostic)Quean
+1, but you don't need Method Injection with a DbContextFactory (or with a Func<DbContext>). You pass the factory/delegate into the constructor, and each method has a using block as you show.Uganda
@TrueWill: that would be about the same as having a transient DbContext. I discussed the downsides of that in my answer.Cottage
According to this blog entry DbContext does not really need to be disposed of.Octachord
But you don't pass the context to decorated class, how the decorated class could work with the same context that passed to the TransactionCommandHandlerDecorator? for example if the decorated class be InsertCommandHandler class, how could it register insert operation to the context(DbContext in EF)?Colorific
@Masoud: You have to make sure the same DbContext instance is reused throughout the object graph you construct. If you build up the object graph manually, you do this be storing the instance in a local field and reuse it. If you use a DI framework, you have to configure the lifestyle of that registration. Usually you would use a scoped lifestyle, such as Per-Web-Request or something similar.Cottage
I know this is old, but what about when you have a complex entity to save? 2-3 levels deep of one to many relationships. EF Seems to have problems with this, and requires a context.SaveChanges() to get the Id of a parent or it will throw a FK Violation.Lightness
@Lightness never had this problem. Might be something wrong with your mapping or it might be a bug in EF. But nonetheless, the story stays the same. A decorator can be used to wrap the operation in a transaction. This allows you to call SaveChanges as much as you like while keeping the operation atomic.Cottage
Can someone explain to me why is the use of DbContextFactory in more detail with a example into controller class. I do not understand the use the DbContextFactory.Asthenia
M
45

There are two contradicting recommendations by microsoft and many people use DbContexts in a completely divergent manner.

  1. One recommendation is to "Dispose DbContexts as soon as posible" because having a DbContext Alive occupies valuable resources like db connections etc....
  2. The other states that One DbContext per request is highly reccomended

Those contradict to each other because if your Request is doing a lot of unrelated to the Db stuff , then your DbContext is kept for no reason. Thus it is waste to keep your DbContext alive while your request is just waiting for random stuff to get done...

So many people who follow rule 1 have their DbContexts inside their "Repository pattern" and create a new Instance per Database Query so X*DbContext per Request

They just get their data and dispose the context ASAP. This is considered by MANY people an acceptable practice. While this has the benefits of occupying your db resources for the minimum time it clearly sacrifices all the UnitOfWork and Caching candy EF has to offer.

Keeping alive a single multipurpose instance of DbContext maximizes the benefits of Caching but since DbContext is not thread safe and each Web request runs on it's own thread, a DbContext per Request is the longest you can keep it.

So EF's team recommendation about using 1 Db Context per request it's clearly based on the fact that in a Web Application a UnitOfWork most likely is going to be within one request and that request has one thread. So one DbContext per request is like the ideal benefit of UnitOfWork and Caching.

But in many cases this is not true. I consider Logging a separate UnitOfWork thus having a new DbContext for Post-Request Logging in async threads is completely acceptable

So Finally it turns down that a DbContext's lifetime is restricted to these two parameters. UnitOfWork and Thread

Moonraker answered 13/12, 2016 at 15:10 Comment(1)
In all fairness, your HTTP requests should be finishing rather quickly (few ms). If they are going longer than that, then you might want to think about doing some background processing with something like an external job scheduler so that the request can return immediately. That said, your architecture shouldn't really rely on HTTP either. Overall, a good answer though.Dowie
L
34

Not a single answer here actually answers the question. The OP did not ask about a singleton/per-application DbContext design, he asked about a per-(web)request design and what potential benefits could exist.

I'll reference http://mehdi.me/ambient-dbcontext-in-ef6/ as Mehdi is a fantastic resource:

Possible performance gains.

Each DbContext instance maintains a first-level cache of all the entities its loads from the database. Whenever you query an entity by its primary key, the DbContext will first attempt to retrieve it from its first-level cache before defaulting to querying it from the database. Depending on your data query pattern, re-using the same DbContext across multiple sequential business transactions may result in a fewer database queries being made thanks to the DbContext first-level cache.

It enables lazy-loading.

If your services return persistent entities (as opposed to returning view models or other sorts of DTOs) and you'd like to take advantage of lazy-loading on those entities, the lifetime of the DbContext instance from which those entities were retrieved must extend beyond the scope of the business transaction. If the service method disposed the DbContext instance it used before returning, any attempt to lazy-load properties on the returned entities would fail (whether or not using lazy-loading is a good idea is a different debate altogether which we won't get into here). In our web application example, lazy-loading would typically be used in controller action methods on entities returned by a separate service layer. In that case, the DbContext instance that was used by the service method to load these entities would need to remain alive for the duration of the web request (or at the very least until the action method has completed).

Keep in mind there are cons as well. That link contains many other resources to read on the subject.

Just posting this in case someone else stumbles upon this question and doesn't get absorbed in answers that don't actually address the question.

Lipfert answered 15/12, 2015 at 23:34 Comment(1)
Good link! Explicit managing the DBContext looks like the safest approach.Aubreyaubrie
B
21

I'm pretty certain it is because the DbContext is not at all thread safe. So sharing the thing is never a good idea.

Browder answered 14/5, 2012 at 15:3 Comment(3)
Do you mean sharing it across HTTP requests is never a good idea?Icecap
Yes Andrew thats what he meant. Sharing the context is only for single thread desktop apps.Nissy
What about sharing the context for one request. So for one request we can have access to different repositories and make a transaction across them by sharing one and the same context?Michiko
C
17

One thing that's not really addressed in the question or the discussion is the fact that DbContext can't cancel changes. You can submit changes, but you can't clear out the change tree, so if you use a per request context you're out of luck if you need to throw changes away for whatever reason.

Personally I create instances of DbContext when needed - usually attached to business components that have the ability to recreate the context if required. That way I have control over the process, rather than having a single instance forced onto me. I also don't have to create the DbContext at each controller startup regardless of whether it actually gets used. Then if I still want to have per request instances I can create them in the CTOR (via DI or manually) or create them as needed in each controller method. Personally I usually take the latter approach as to avoid creating DbContext instances when they are not actually needed.

It depends from which angle you look at it too. To me the per request instance has never made sense. Does the DbContext really belong into the Http Request? In terms of behavior that's the wrong place. Your business components should be creating your context, not the Http request. Then you can create or throw away your business components as needed and never worry about the lifetime of the context.

Celebrate answered 25/2, 2016 at 1:14 Comment(5)
This is an interesting answer and I partially agree with you. To me, a DbContext doesn't have to be tied to a web request, but it IS always typed to one single 'request' as in: 'business transaction'. And when you tie the context to a business transaction, change cancellation becomes really weird to do. But not having it on web request boundary doesn't mean that the business components (BCs) should be creating the context; I think that is not their responsibility. Instead, you can apply scoping using decorators around your BCs. This way you can even change scoping without any code change.Cottage
Well in that case the injection into the business object should deal with the lifetime management. In my view the business object owns the context and as such should control the lifetime.Celebrate
In brief, what do you mean when you say "the ability to recreate the context if required"? are you rolling your own rollback ability? can you elaborate a tad?Shang
Personally, I think it is a bit troublesome to force a DbContext at the start there. There is no guarantee that you even need to hit the database. Maybe you are calling a 3rd party service that changes state on that side. Or maybe you actually have 2 or 3 databases you are working with at the same time. You wouldn't create a bunch of DbContexts at the start just in case you end up using them. The business knows the data it is working with, so it belongs with that. Just put a TransactionScope at the start if it is needed. I don't think all calls need one. It does take resources.Seppala
Thats the question of whether you allow the container to control lifetime of the dbcontext which then controls lifetime of parent controls, sometimes unduly. Say if I want a simple service singleton injected into my controllers then I will not be able to use constuctor inject due to per request semantic.Spa
H
12

I agree with previous opinions. It is good to say, that if you are going to share DbContext in single thread app, you'll need more memory. For example my web application on Azure (one extra small instance) needs another 150 MB of memory and I have about 30 users per hour. Application sharing DBContext in HTTP Request

Here is real example image: application have been deployed in 12PM

Hellcat answered 8/4, 2013 at 13:36 Comment(1)
Possibly the idea is to share the context for one request. If we access different repositories and - DBSet classes and want the operations with them to be transactional that should be a good solution. Have a look at the open source project mvcforum.com I think that is done in their implementation of Unit Of Work design pattern.Michiko
S
3

What I like about it is that it aligns the unit-of-work (as the user sees it - i.e. a page submit) with the unit-of-work in the ORM sense.

Therefore, you can make the entire page submission transactional, which you could not do if you were exposing CRUD methods with each creating a new context.

Scaliger answered 14/5, 2012 at 14:58 Comment(0)
C
3

Another understated reason for not using a singleton DbContext, even in a single threaded single user application, is because of the identity map pattern it uses. It means that every time you retrieve data using query or by id, it will keep the retrieved entity instances in cache. The next time you retrieve the same entity, it will give you the cached instance of the entity, if available, with any modifications you have done in the same session. This is necessary so the SaveChanges method does not end up with multiple different entity instances of the same database record(s); otherwise, the context would have to somehow merge the data from all those entity instances.

The reason that is a problem is a singleton DbContext can become a time bomb that could eventually cache the whole database + the overhead of .NET objects in memory.

There are ways around this behavior by only using Linq queries with the .NoTracking() extension method. Also these days PCs have a lot of RAM. But usually that is not the desired behavior.

Cretin answered 6/8, 2015 at 18:52 Comment(2)
This is correct, but you have to assume the Garbage Collector will work, making this problem more virtual than actual.Delfeena
Garbage collector is not going to collect any object instances held by an active static/singleton object. They will end up in the gen 2 of the heap.Cretin
O
1

Another issue to watch out for with Entity Framework specifically is when using a combination of creating new entities, lazy loading, and then using those new entities (from the same context). If you don't use IDbSet.Create (vs just new), Lazy loading on that entity doesn't work when its retrieved out of the context it was created in. Example:

 public class Foo {
     public string Id {get; set; }
     public string BarId {get; set; }
     // lazy loaded relationship to bar
     public virtual Bar Bar { get; set;}
 }
 var foo = new Foo {
     Id = "foo id"
     BarId = "some existing bar id"
 };
 dbContext.Set<Foo>().Add(foo);
 dbContext.SaveChanges();

 // some other code, using the same context
 var foo = dbContext.Set<Foo>().Find("foo id");
 var barProp = foo.Bar.SomeBarProp; // fails with null reference even though we have BarId set.
Overreact answered 23/3, 2016 at 17:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.