Entity Framework Транзакция с несколькими потоками - PullRequest
7 голосов
/ 10 мая 2011

У меня есть приложение с несколькими потоками.Потоки НЕ разделяют ObjectContext (каждый поток имеет свой собственный - я знаю, что они не являются потокобезопасными).

Однако все потоки работают в общей транзакции.Исходный поток создает TransactionScope, а каждый поток, который он порождает, создает TransactionScope, используя DependentTransaction из транзакции в основном потоке.

Когда несколько запросов ObjectContext выполняются одновременно, я иногда (не всегда) получаю ошибку:

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: 

Я только знаю, что они работают одновременно, потому что, когда я запускаю свои модульные тесты в режиме отладки, и появляется это исключение, если я смотрю на разные запущенные потоки, я всегда вижупо крайней мере один другой поток остановился при операции ObjectContext.

Кроме того, после некоторого чтения я попытался добавить multipleactiveresultsets=False в строку подключения, и это не имело никакого значения.

Это ошибкав Entity Framework?

Ответы [ 2 ]

2 голосов
/ 27 мая 2011

Проблема описана здесь:

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

Достаточно просто заблокировать вызовы SaveChanges и Refresh, но для того, чтобы убедиться, что блокировки происходят во время выполнения запроса, мне пришлось создать фиктивный поставщик запросов, который блокируется при выполнении запросов. Мне действительно не следовало этого делать. Entity Framework должен был быть достаточно надежным, чтобы справиться с этим из коробки ... особенно если учесть, что вы не должны заниматься созданием собственного соединения.

Вот код для оболочки поставщика запросов. Сами IQueryables и базовый класс QueryProvider представляют собой простые реализации многократного использования, основанные здесь 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);
            }
        }
    }
0 голосов
/ 26 мая 2011

Если вы передаете один и тот же экземпляр зависимого клона нескольким потокам, а затем распределяете их в каждом потоке, это может привести к такому поведению (например, фиксация завершенной транзакции и т. Д.). AFAIK, тебе нужен отдельный зависимый клон для каждой нити.

Другая возможность заключается в том, что «родительская» транзакция завершается или удаляется до того, как потоки завершат свою транзакцию. Убедитесь, что ваша асинхронная работа выполнена, прежде чем вы покинете основную TranscationScope (хотя это можно установить для блокировки незавершенных дочерних транзакций).

Извините, не более того с тем, что вы описали.

Удачи, Michael

...