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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With