One Dbcontext Per Web Request... Why

One DbContext per web request... why?

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 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, were 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.

Why is DbContext per-request?

In the simplest examples where you have a GET and POST method in a controller, and nothing more, there is no difference between using Transient and Per-Request. Where Per-Request comes in is when your controller hands off to other classes to do related work. A common example is a repository pattern, but really any service/helper class that centralizes shared behaviour applies as well. If the DbContext is Transient and you call one of these services passing an entity that the controller had retrieved from the DbContext and those services access a DbContext, they will be using a different instance of a DbContext than the Controller did. This can lead to pretty annoying issues with entity tracking and attempts to insert duplicate rows because the controller's DbContext instance isn't tracking entities that might have been loaded by another DbContext and associated to the entity the controller's instance was tracking. By using an instance per-request, any associated class that requests a DbContext will receive the same instance which negates these issues.

Even if all operations are somewhat atomic from each other, having a single instance means that these changes will all be committed or rolled back together. While this is typically a desired behaviour, this can itself lead to unexpected issues because the question comes up as to when should SaveChanges be called? When you have a controller calling various common methods and a DbContext is scoped per-request, if any of those methods call SaveChanges, everything done up to that point would be attempted to commit to the DB. So you can run into issues where you call 3 common service methods that each call a SaveChanges, the first 2 succeed with no issue, but the 3rd one fails. Typically you expect the Controller to have the final say to perform the commit and handle the exceptions. In these cases it can be beneficial to utilize a Unit of Work to "encase" the DbContext and ensure that the commit or rollback decision of the DbContext is maintained at the highest level of the operation. This way the UoW can be scoped at the start of the request (I.e. Controller method), and the supporting services etc. receive their DbContext reference from the UoW, but the controller gets the final say if/when the UoW SaveChanges gets called.

If using one DbContext instance per request, should I dispose the context on each request as well:

The context is designed for short time lifetime. Usually you see it in

using(var ctx = new DbContext())
{
...
}

Which means ctx.Dispose is called just after end of use.

More details here, see "lifetime" section.

So regarding your code, if you aren't using dependency injection (In which case you would have configured dbcontext as per request) you are doing well disposing it after action execution.

The only concern would be what if you call child actions.. In that case you may end having more than one dbcontext per request

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

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; }
}
}

Transaction per request in multiple dbcontext in EntityFramework

I have done this,

The crucial thing is to take control of database connection life-cycle away from EF, and take care of connection initialization, opening, closing and disposal yourself.

To do this, you will use following base DbContext constructor :DbContext(DbConnection connection, Boolean contextOwnsConnection)

Constructs a new context instance using the existing connection to
connect to a database. The connection will not be disposed when the
context is disposed if contextOwnsConnection is false.

You should expose constructor with DbConnection in all your application contexts and inject same connection you created outside DbContext. This way EF will not create and open them.

Finally, in your connection manager class you can use DbConnection.BeginTransaction() to get DbTransaction object and work with it according to your needs.

Below is draft of required changes of your class, to get the idea across.

public partial class ContextDB : DbContext
{
// New constructor
public ContextDB(DbConnection connection)
: base(connection, false)
{
}
}

public class TransactionPerRequest :
IRunOnEachRequest, IRunOnError, IRunAfterEachRequest
{
private readonly ContextDB _Context;
private readonly HttpContextBase _HttpContext;
private readonly DbConnection _cnn;

public TransactionPerRequest(HttpContextBase httpContext)
{
// Your code creates the connection
_cnn = new SqlConnection("Data Source=.;Initial Catalog=DB;Integrated Security=SSPI;");
// Pass connection your context
_Context = new ContextDB(_cnn);
_HttpContext = httpContext;
}

void IRunOnEachRequest.Execute()
{
// Open connection
_cnn.Open();
_HttpContext.Items["_Transaction"] =
_cnn.BeginTransaction(IsolationLevel.ReadCommitted);
}

void IRunOnError.Execute()
{
_HttpContext.Items["_Error"] = true;
}

void IRunAfterEachRequest.Execute()
{
var transaction = (DbContextTransaction)_HttpContext.Items["_Transaction"];

if (_HttpContext.Items["_Error"] != null)
transaction.Rollback();
else
transaction.Commit();

_cnn.Close();
_cnn.Dispose();
}
}


Related Topics



Leave a reply



Submit