【问题标题】:Issue with TransactionScope and async calls with EF6TransactionScope 的问题和 EF6 的异步调用
【发布时间】:2016-02-10 17:44:25
【问题描述】:

我有以下代码,旨在将批量 EF 保存分成更小的块,表面上是为了提高性能。

var allTasks = arrayOfConfigLists
        .Select(configList =>
            Task.Run(() => SaveConfigurations(configList))
        .ToArray();

Task.WaitAll(allTasks);

对 SaveConfigurations 的每次调用都会创建一个运行到完成的新上下文。

private static void SaveConfigurations(List<Configuration> configs)
{
    using (var dc = new ConfigContext())
    {
        dc.Configuration.AutoDetectChangesEnabled = false;
        dc.SaveConfigurations(configs);
    }
}

就目前而言,代码运行相对高效,考虑到这可能不是最佳的做事方式。但是,如果其中一个 SaveConfigurations 失败,我意识到我需要回滚保存到数据库的任何其他配置。

经过一些研究,我将现有框架升级到 4.5.1,并利用新的 TransactionScopeAsyncFlowOption.Enabled 选项来处理异步调用。我做了以下更改:

using (var scope = 
    new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
    //... allTasks code snippet from above
    scope.Complete();
}

此时,我开始汇总各种有趣的错误:

该操作对于事务的状态无效。

底层提供程序在打开时失败。

分布式事务管理器 (MSDTC) 的网络访问已被禁用。

事务管理器已禁用对远程/网络事务的支持。

我不明白为什么引入 TransactionScope 会产生这么多问题。我假设我对异步调用如何与 EF 交互以及 TransactionScope 如何包装这些调用存在根本性的误解,但我无法弄清楚。而且我真的不知道 MSDTC 异常与什么有关。

关于如何通过对同一个数据库进行异步调用来实现回滚功能有什么想法吗?有没有更好的方法来处理这种情况?

更新: 查看文档here 后,我发现 Database.BeginTransaction() 是首选的 EF 调用。但是,这假设我的所有更改都将发生在同一上下文中,但事实并非如此。没有创建一个虚拟上下文和传递一个事务,我认为这不能解决我的问题。

【问题讨论】:

    标签: c# entity-framework asynchronous transactions rollback


    【解决方案1】:

    这与异步无关。您正在编写多个连接并希望它是原子的。这需要分布式事务。没有办法解决这个问题。

    您也可能会遇到这种只能通过超时解决的分布式死锁。

    也许,最好的方法是停止使用多个连接。如果性能是这样一个问题,请考虑使用一种众所周知的不涉及 EF 的批量 DML 技术进行写入。

    您可以使用 MARS 在同一连接上进行并发写入,但它们实际上是在服务器上串行执行的。尽管由于流水线效应,这将提供一个小的加速。可能不值得麻烦

    【讨论】:

    • 感谢您的建议。我们办公室没有专门的数据库程序员,所以所有这些问题对我来说都是新的。我猜 TransactionScope 是红鲱鱼?似乎多个上下文调用在没有它的情况下也能正常工作,但 SQL Server 一定是在幕后处理它们。我将只使用一个连接运行一些测试,看看它是如何进行的。我最多只插入约 10,000 行,所以我觉得处理时间 > 60 秒是不合理的。
    • 不,涉及 TransactionScope。没有它,更改不是原子的,这是不正确的。有了它,更改是原子的,但需要 MSDTC(最好避免这样做,原因有几个)。
    • I'm only inserting ~10,000 rows at worst, so I feel like processing times &gt; 60 seconds is unreasonable. 如果您想解决这个问题,请提出新问题并在此处留下链接。使用 SQL Profiler 捕获执行的 SQL 并发布实际执行计划。
    【解决方案2】:

    这个怎么样

    这只会创建一个上下文,即 attach entities 到上下文。 See entity framework bulk insertion

    如果插入出现任何问题,整个事务将被回滚。如果您想要更多类似模式的事务,请实现Unit of work pattern

    据我所知Entity framework 本身具有工作单元模式。

     public SaveConfigurations(List<Configuration> configs)
        {
            try
            {
    
                using (var dc = new ConfigContext())
                {
                   dc.Configuration.AutoDetectChangesEnabled = false;
                   foreach(var singleConfig in configs)
                   {
                     //Donot invoke dc.SaveChanges on the loop.
                     //Assuming the SaveConfiguration is your table.
                     //This will add the entity to DbSet<T> , Will not insert to Db until you invoke SaveChanges
                     dc.SaveConfiguration.Add(singleConfig);
                   } 
                   dc.Configuration.AutoDetectChangesEnabled = true;
                   dc.SaveChanges();
                }
    
            }
            catch (Exception exception)
            {
               throw exception
            }
    
        }
    

    【讨论】:

    • 感谢您的建议!我将对工作单元模式进行一些研究并尝试一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    相关资源
    最近更新 更多