具有多个线程的实体框架事务

时间:2011-05-09 21:51:40

标签: c# multithreading entity-framework transactions transactionscope

我有一个运行多个线程的应用程序。线程不共享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添加到我的连接字符串中,这没有任何区别。

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

2 个答案:

答案 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之前,请确保您的异步工作已完成(尽管可以将其设置为在未完成的子事务上阻止)。

抱歉,与您所描述的内容相比,没有更多内容。

祝你好运, 迈克尔