Jeff Jeff - 14 days ago 9
C# Question

Entity Framework Transaction With Multiple Threads

I have an application running multiple threads. The threads do NOT share an ObjectContext (each thread has its own - I know they are not thread safe).

However, the threads are all operating under a shared Transaction. The original thread creates a TransactionScope and each thread it spawns creates a TransactionScope using a DependentTransaction from the Transaction on the main thread.

When multiple ObjectContext requests run at the same time, I sometimes (not consistently) get the error:

System.Data.EntityException occurred
Message=An error occurred while closing the provider connection. See the inner exception for details.

InnerException: System.Transactions.TransactionException
Message=The operation is not valid for the state of the transaction.
at System.Transactions.TransactionStatePSPEOperation.get_Status(InternalTransaction tx)
at System.Transactions.TransactionInformation.get_Status()
at System.Data.ProviderBase.DbConnectionInternal.CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlInternalConnection.CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
at System.Data.SqlClient.SqlConnection.Close()
at System.Data.EntityClient.EntityConnection.StoreCloseHelper()

I only know they are running at the same time because when I run my unit tests in debug mode and this exception pops up, if I look at the different threads that are running, I always see at least one other thread halted at an ObjectContext operation.

Also, after doing some reading, I tried adding
to my connection string and that made no difference.

Is this a bug in Entity Framework?


The problem is described here:

It's easy enough to lock the SaveChanges and Refresh calls, but in order to make sure locks occur during query execution I had to make a dummy query provider that locks when executing queries. I really shouldn't have had to do this. Entity Framework should have been robust enough to handle this out of the box...especially considering you're not meant to handle your own connection creation.

Here is the code for the query provider wrapper. The IQueryables themselves and the base QueryProvider class are simple reusable implementations based off here

    /// <summary>
    /// A wrapper for queries executed by EF.
    /// </summary>
    internal class EntityFrameworkQueryProvider : QueryProvider
        protected override object Execute(Expression expression)
                // this is required due to a bug in how EF multi-threads when Transactions are used.
                if (Transaction.Current != null) Monitor.Enter(EntityFrameworkExtensions.SyncRoot);

                // enumerate is a simple extension method that forces enumeration of the IQueryable, thus making it actually get executed during the lock
                return Expression.Lambda(expression).Compile().DynamicInvoke().Enumerate();
                if (Transaction.Current != null) Monitor.Exit(EntityFrameworkRepositoryProvider.SyncRoot);