我有一个运行多个线程的应用程序。线程不共享ObjectContext(每个线程都有自己的 - 我知道它们不是线程安全的)。
但是,线程都在共享事务下运行。原始线程创建一个TransactionScope,它生成的每个线程使用主线程上的Transaction中的DependentTransaction创建一个TransactionScope。
当多个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
添加到我的连接字符串中,这没有任何区别。
这是实体框架中的错误吗?
答案 0 :(得分:2)
问题在这里描述:
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);
}
}
}
答案 1 :(得分:0)
如果要将依赖克隆的相同实例传递给多个线程,然后在每个线程上处理它们,那么可能会导致这样的行为(例如,提交已完成的事务等)。 AFAIK,每个线程需要一个单独的依赖克隆。
另一种可能性是“父”事务在线程完成事务之前完成或处理。在离开主TranscationScope之前,请确保您的异步工作已完成(尽管可以将其设置为在未完成的子事务上阻止)。
抱歉,与您所描述的内容相比,没有更多内容。
祝你好运, 迈克尔