具有多个线程的实体框架事务
本文关键字:实体 框架 事务 线程 | 更新日期: 2023-09-27 17:55:57
我有一个运行多个线程的应用程序。线程不共享 ObjectContext(每个线程都有自己的线程 - 我知道它们不是线程安全的)。
但是,线程都在共享事务下运行。原始线程创建一个事务范围,它生成的每个线程使用来自主线程上的事务的依赖事务创建一个事务范围。
当多个 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
添加到我的连接字符串中,这没有任何区别。
这是实体框架中的错误吗?
下面描述了这个问题:
http://www.b10g.dk/2007/09/07/dependenttransaction-and-multithreading/
锁定 SaveChanges 和 Refresh 调用很容易,但为了确保在查询执行期间发生锁定,我必须创建一个在执行查询时锁定的虚拟查询提供程序。我真的不应该这样做。实体框架应该足够强大,可以开箱即用地处理这个问题......特别是考虑到您不应该处理自己的连接创建。
下面是查询提供程序包装器的代码。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,您需要每个线程一个单独的依赖克隆。
另一种可能性是"父"事务在线程完成其事务之前完成或释放。在离开主TranscationScope之前,请确保您的异步工作已完成(尽管这可以设置为阻止未完成的子事务)。
对不起,你描述的内容没有更多。
祝你好运迈克尔