4

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.

2 Answers 2

9
+100

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, that fixed it, I was missing the scope2.complete()
0

How about this syntax, this is pretty similar , Scope2 is Completed and Disposed of later.

I'm also intermittently seeing the "cannot access a disposed of object 'Transaction' error. Could it be because I should have created both of these with TransactionScopeOption.RequiresNew instead of TransactionScopeOption.Required?

 TransactionOptions rootOptions = new TransactionOptions();
    rootOptions.IsolationLevel = IsolationLevel.ReadCommitted;
    OtherObject.Scope2 = new TransactionScope(TransactionScopeOption.Required, rootOptions);

    TransactionOptions options = new TransactionOptions();
    options.IsolationLevel = IsolationLevel.ReadCommitted;
    options.Timeout = getTransactionTimeout();
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, options))
                {

                                .............
                    scope.Complete();
                }

1 Comment

Hi, I also see that intermitent "Cannot access a disposed object. Object name: 'TransactionScope'. Did you fix also that?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.