Saeid Mirzaei
Saeid Mirzaei

Reputation: 1236

Transaction per request in multiple dbcontext in EntityFramework

I'm using Asp.Net MVC and Entity Framework. I have a class to manage transaction per request as follow:

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

    public TransactionPerRequest(ContextDB context,
        HttpContextBase httpContext)
    {
        _Context = context;
        _HttpContext = httpContext;
    }

    void IRunOnEachRequest.Execute()
    {
        _HttpContext.Items["_Transaction"] =
            _Context.Database.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();
    }
}

I want to use multiple contexts as explained here

How I can change the TransactionPerRequset class?

Upvotes: 2

Views: 2079

Answers (1)

Edgars Pivovarenoks
Edgars Pivovarenoks

Reputation: 1684

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

Upvotes: 2

Related Questions