Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.
       Source=System.Transactions
       StackTrace:
            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()
       InnerException: 

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 multipleactiveresultsets=False to my connection string and that made no difference.

Is this a bug in Entity Framework?

like image 239
Jeff Avatar asked May 09 '11 21:05

Jeff


People also ask

Is it possible to create a new transaction in Entity Framework?

[SOLVED] => Entity Framework - "New transaction is not allowed... System.Data.SqlClient.SqlException: New transaction is not allowed because there are other threads running in the session.

Is Entity Framework thread safe for MVC?

Entity Framework is not thread-safe. An MVC controller is instantiated per request . Thus if you use one DbContext per request, you're safe as long as you don't manually spawn threads in your controller actions (which you shouldn't do anyway).

What is TransactionScope in Entity Framework?

Entity Framework already has an existing transaction Entity Framework is already operating within a TransactionScope The connection object in the transaction passed is null. That is, the transaction is not associated with a connection – usually this is a sign that that transaction has already completed

What happens if we use multiple threads for the transaction?

What happens if we use multiple threads for the transaction is that these thread local values are not visible across multiple threads. Therefore, Spring cannot maintain the transaction state throughout the transaction. Now you understand the problem with spring transactions over multiple threads.


1 Answers

The problem is described here:

http://www.b10g.dk/2007/09/07/dependenttransaction-and-multithreading/

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 http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx

    /// <summary>
    /// A wrapper for queries executed by EF.
    /// </summary>
    internal class EntityFrameworkQueryProvider : QueryProvider
    {    
        protected override object Execute(Expression expression)
        {
            try
            {
                // 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();
            }
            finally
            {
                if (Transaction.Current != null) Monitor.Exit(EntityFrameworkRepositoryProvider.SyncRoot);
            }
        }
    }
like image 150
Jeff Avatar answered Sep 27 '22 18:09

Jeff