7

我有一个应用程序运行多个线程。线程不共享ObjectContext(每个线程都有自己的 - 我知道它们不是线程安全的)。实体框架事务与多个线程

然而,线程都共享事务下操作。原始线程创建一个TransactionScope并且它产生的每个线程使用主线程上的事务处理中的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我的连接字符串并没有什么区别。

这是实体框架中的错误吗?

回答

1

的问题说明如下:

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

这是很容易锁定的SaveChanges和刷新电话,但为了确保锁查询执行过程中发生在执行查询时,我必须制作一个虚拟查询提供程序来锁定它。我真的不应该这样做。实体框架应该已经足够健壮以处理这种开箱即用的问题......特别是考虑到您不打算处理自己的连接创建。

下面是查询提供包装的代码。该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

您的答案基于您的链接不再工作。你能否提供更多关于根本原因的信息? – 2016-07-19 10:44:23

+1

用于输入锁定的SyncRoot与离开时使用的SyncRoot不同。 – 2016-07-19 10:47:06

0

如果您传递相关克隆的同一个实例,以多个线程,然后在每个线程上处置他们,这可能导致这样的行为(如提交完成的交易,这样的)。 AFAIK,你需要一个单独的从属克隆每个线程。

另一种可能性是“父”的事务是越来越完结或之前的螺纹与他们的交易完成。确保在离开主TranscationScope之前完成异步工作(尽管可以设置为阻止未完成的子事务)。

对不起,没有比这更与你所描述的东西。

祝你好运, 迈克尔

+0

我所有的新主题交易的依赖克隆和之前的任何子线程的父线程没有完成交易。任何其他想法?谢谢。 – Jeff 2011-05-26 21:49:51