patterncsharpCritical
One DbContext per web request... why?
Viewed 0 times
webdbcontextwhyrequestperone
Problem
I have been reading a lot of articles explaining how to set up Entity Framework's
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
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?Solution
NOTE: This answer talks about the Entity Framework's
it is applicable to any sort of Unit of Work implementation, such as
LINQ to SQL's
Let's start by echoing Ian: Having a single
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
Let me start by saying that registering a
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 consider:
Another option is to not inject a
The plus side of this is that you manage the life of the
The downside is that you will have to pass around the
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 happen in the past). For a simple application, this approach could work though.
Because of the downsides this factory approach has, 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
DbContext, butit is applicable to any sort of Unit of Work implementation, such as
LINQ to SQL's
DataContext, and NHibernate's ISession.Let's 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 caches data, and it gets stale pretty soon. This will get you in all sorts of trouble when multiple users or application instances work on that database through the use of the DbContext (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 consider:
- As 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, because everyDbContextinstance has its own set of changes. 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.
- As
DbContextimplementsIDisposable, you probably still want to dispose off all created instances. If you want to do this, you basically have two options. You need to dispose off them in the same method right after callingcontext.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 off 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 off.
Another option is to not inject a
DbContext at all. Instead, you inject some sort of DbContextFactory that is able to create a new instance. This way the business logic controls the context explicitly. It 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 a form of 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 off 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 happen in the past). For a simple application, this approach could work though.
Because of the downsides this factory approach has, 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
Code Snippets
public void SomeOperation()
{
using (var context = this.contextFactory.CreateNew())
{
var entities = this.otherDependency.Operate(
context, "some value");
context.Entities.InsertOnSubmit(entities);
context.SaveChanges();
}
}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();
}
}Context
Stack Overflow Q#10585478, score: 635
Revisions (0)
No revisions yet.