简体   繁体   English

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

[英]Entity Framework Transaction With Multiple Threads

I have an application running multiple threads. 我有一个运行多个线程的应用程序。 The threads do NOT share an ObjectContext (each thread has its own - I know they are not thread safe). 线程不共享ObjectContext(每个线程都有自己的 - 我知道它们不是线程安全的)。

However, the threads are all operating under a shared Transaction. 但是,线程都在共享事务下运行。 The original thread creates a TransactionScope and each thread it spawns creates a TransactionScope using a DependentTransaction from the Transaction on the main thread. 原始线程创建一个TransactionScope,它生成的每个线程使用主线程上的Transaction中的DependentTransaction创建一个TransactionScope。

When multiple ObjectContext requests run at the same time, I sometimes (not consistently) get the error: 当多个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: 

I only know they are running at the same time because when I run my unit tests in debug mode and this exception pops up, if I look at the different threads that are running, I always see at least one other thread halted at an ObjectContext operation. 我只知道它们同时运行,因为当我在调试模式下运行单元测试并弹出此异常时,如果我查看正在运行的不同线程,我总是看到在ObjectContext操作中至少有一个其他线程停止。

Also, after doing some reading, I tried adding multipleactiveresultsets=False to my connection string and that made no difference. 此外,在做了一些阅读之后,我尝试将multipleactiveresultsets=False添加到我的连接字符串中,这没有任何区别。

Is this a bug in Entity Framework? 这是实体框架中的错误吗?

The problem is described here: 问题在这里描述:

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

It's easy enough to lock the SaveChanges and Refresh calls, but in order to make sure locks occur during query execution I had to make a dummy query provider that locks when executing queries. 锁定SaveChanges和Refresh调用很容易,但是为了确保在查询执行期间发生锁定,我必须创建一个在执行查询时锁定的虚拟查询提供程序。 I really shouldn't have had to do this. 我真的不应该这样做。 Entity Framework should have been robust enough to handle this out of the box...especially considering you're not meant to handle your own connection creation. 实体框架应该足够强大,可以开箱即用......特别是考虑到你并不打算处理你自己的连接创建。

Here is the code for the query provider wrapper. 这是查询提供程序包装器的代码。 The IQueryables themselves and the base QueryProvider class are simple reusable implementations based off here http://blogs.msdn.com/b/mattwar/archive/2007/07/30/linq-building-an-iqueryable-provider-part-i.aspx 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);
            }
        }
    }

If you are passing the same instance of the dependent clone to multiple threads, and then disposing of them on each thread, that could lead to behavior like this (eg committing a finished transaction and such). 如果要将依赖克隆的相同实例传递给多个线程,然后在每个线程上处理它们,那么可能会导致这样的行为(例如,提交已完成的事务等)。 AFAIK, you need a separate dependent clone per thread. AFAIK,每个线程需要一个单独的依赖克隆。

Another possibility is the "parent" transaction is getting completed or disposed before the threads finish with their transaction. 另一种可能性是,在线程完成交易之前,“父”交易正在完成或处置。 Make sure your async work is done before you leave the main TranscationScope (although this can be set to block on unfinished child transactions). 离开主TranscationScope之前,请确保您的异步工作已完成(尽管可以将此设置为在未完成的子事务上阻止)。

Sorry, don't have more than that with what you've described. 对不起,没有超过您所描述的内容。

Good luck, Michael 祝你好运,迈克尔

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM