Транзакция Entity Framework С Несколькими Потоками
у меня есть приложение с несколькими потоками. Потоки не разделяют ObjectContext (каждый поток имеет свой собственный - Я знаю, что они не являются потокобезопасными).
однако все потоки работают под общей транзакцией. Исходный поток создает TransactionScope и каждый поток, который он порождает, создает TransactionScope с помощью DependentTransaction из транзакции в основном потоке.
когда несколько запросов ObjectContext выполняются одновременно, I иногда (не последовательно) получить ошибку:
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
к моей строке подключения, и это не имело никакого значения.
это ошибка в сущности Рамки?
2 ответов
проблема описана здесь:
http://www.b10g.dk/2007/09/07/dependenttransaction-and-multithreading/
достаточно легко заблокировать SaveChanges и обновить вызовы, но чтобы убедиться, что блокировки происходят во время выполнения запроса, я должен был сделать фиктивный поставщик запросов, который блокирует при выполнении запросов. Мне не стоило этого делать. 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);
}
}
}
Если вы передаете один и тот же экземпляр зависимого клона нескольким потокам, а затем избавляетесь от них в каждом потоке, это может привести к такому поведению (например, совершение готовой транзакции и тому подобное). AFAIK, вам нужен отдельный зависимый Клон для каждого потока.
другая возможность - "родительская" транзакция завершается или удаляется до того, как потоки завершат свою транзакцию. Убедитесь, что ваша асинхронная работа выполнена, прежде чем покинуть main TranscationScope (хотя это может быть установлено для блокировки незавершенных дочерних транзакций).
извините, не имею больше, чем это с тем, что вы описали.
удачи, Майкл!--1-->