Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot access a disposed object. Transaction

We are using Entity Framework and running unit tests within a transaction scope. We were origianally getting the error in the title.

I have managed to isolate the problem some what.

using (TransactionScope scope1 = new TransactionScope())
{
    using (TransactionScope scope2 = new TransactionScope())
    {
           // Here there is no code
    }

    using (Entities se = new Entities())
    {
        EntityConnection entityConnection = (EntityConnection)se.Connection;
        DbConnection storeConnection = entityConnection.StoreConnection;

        storeConnection.Open(); // On this line the error occurs

           // Some code that runs a stored procedure
    }
}

The error that we are currently getting is "The operation is not valid for the state of the transaction.."

If I remove transaction scope2, everything works fine.

If I mark scope 2 as an ambient transaction it also works fine.

like image 214
Shiraz Bhaiji Avatar asked Jan 24 '23 06:01

Shiraz Bhaiji


1 Answers

You are creating scope2 without an explicit TransactionScopeOption parameter, which yields a default of TransactionScopeOption.Required, see section Remarks within TransactionScope Constructor

This constructor creates a new transaction scope with the transaction scope option equal to Required. This means that a transaction is required by the new scope and the ambient transaction is used if one already exists. Otherwise, it creates a new transaction before entering the scope.

In your example an ambient TransactionScope does indeed exist already (scope1), consequently the new nested TransactionScope (scope2) with implicit parameter TransactionScopeOption.Required is using the existing ambient transaction rather than creating a new transaction itself.

However, the implicit transaction semantics of scope2 are still in place, consequently the existing ambient transaction created by scope1 is getting aborted because you are not calling Complete at the end of scope2:

Failing to call this method aborts the transaction, because the transaction manager interprets this as a system failure, or equivalent to an exception thrown within the scope of transaction

Of course the problem goes aways immediately if you remove scope2 or change its semantics to TransactionScopeOption.RequiresNew (meaning 'A new transaction is always created for the scope.'), because the existing ambient transaction created by scope1 will not be affected anymore.

See Implementing an Implicit Transaction using Transaction Scope for more details on this.

like image 182
Steffen Opel Avatar answered Jan 30 '23 14:01

Steffen Opel