具有实体框架并行上下文的事务范围

时间:2017-10-06 09:16:23

标签: c# entity-framework parallel-processing transactionscope

        using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
        {
            var id = ((Func<int>)delegate ()
            {
                using (var context = new MyContext())
                {
                    var order = new Order { Name = "Test", Flags = 1 };
                    context.Orders.Add(order);
                    context.SaveChanges();
                    return order.ID;
                }
            }).Invoke();

            Enumerable.Range(1, 10).AsParallel().ForAll(x =>
            {
                using (var context = new MyContext())
                {
                    var order = context.Orders.FirstOrDefault(order => order.ID == id);

                    if (order == null)
                    {
                        Console.WriteLine($"Thread: {x} could not find order.");
                    }
                    else
                    {
                        Console.WriteLine($"Thread: {x} found order.");
                        //TODO: further processing.
                    }
                }
            });

            scope.Complete();
        }

我正在尝试在并行环境中从上下文创建,更新和获取实体。正如您可以看到上面的代码,一旦我使用并行循环外的上下文创建订单然后在并行循环内部我尝试检索新创建的订单但只有一个或两个线程能够找到它。

我尝试使用不同级别的 IsolationLevel ,但结果仍然相同。

此实施有问题吗?

2 个答案:

答案 0 :(得分:1)

正如TransactionScope documentation所述,在处理多线程场景时,您还应该使用DependentTransaction

来自catch (ex) { throw ex; } MSDN page

  

您还应该使用TransactionScope和DependentTransaction   需要使用同一事务的应用程序的类   跨多个函数调用或多个线程调用。

答案 1 :(得分:0)

如果您实现@polkduran在MSDN上建议的内容,您将开始获取与数据库连接相关的System.Data.Entity.Core.EntityExceptions。因此,当我为创建读取DbContext的代码添加一个锁时,异常停止发生:

    object locker = new object();

    using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted }))
    {
        int id;
        using (var context = new MyContext())
        {
            var order = new Order { Name = "Test", Flags = 1 };
            context.Orders.Add(order);
            context.SaveChanges();
            id = order.Id;
        }

        var transactionToUse = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

        Enumerable.Range(1, 10).AsParallel().ForAll(x =>
        {
            lock (locker)
            {
                using (var ts = new TransactionScope(transactionToUse))
                {
                    using (var context = new MyContext())
                    {
                        var orderItem = context.Orders.FirstOrDefault(order => order.Id == id);

                        if (orderItem == null)
                        {
                            Console.WriteLine($"Thread: {x} could not find order.");
                        }
                        else
                        {
                            Console.WriteLine($"Thread: {x} found order.");
                            //TODO: further processing.
                        }
                    }
                    ts.Complete();
                }
            }    
        });

        scope.Complete();
    }

我猜多个具有不同连接状态的线程的共享连接会为创建的每个DbContext实例创建竞争条件。