C# TransactionScope问题和EF6的异步调用

C# TransactionScope问题和EF6的异步调用,c#,entity-framework,asynchronous,transactions,rollback,C#,Entity Framework,Asynchronous,Transactions,Rollback,我有以下代码,旨在将大容量EF保存分解为更小的块,表面上是为了提高性能 var allTasks = arrayOfConfigLists .Select(configList => Task.Run(() => SaveConfigurations(configList)) .ToArray(); Task.WaitAll(allTasks); 每次调用SaveConfigurations都会创建一个新的上下文,该上下文将

我有以下代码,旨在将大容量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);
    }
}
此时,我开始汇总各种有趣的错误:

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

基础提供程序在打开时失败

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

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

我不明白的是,为什么引入TransactionScope会产生这么多问题。我假设我对异步调用如何与EF交互以及TransactionScope如何包装这些调用有一个基本的误解,但我无法理解。我真的不知道MSDTC例外与什么有关

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

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

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

您还可能以这种方式遇到分布式死锁,而这种死锁只能通过超时来解决

最好的方法可能是停止使用多个连接。如果性能是这样一个问题,考虑使用一个众所周知的批量DML技术来编写,它不涉及EF。 您可以使用MARS在同一个连接上进行并发写入,但它们实际上是在服务器上串行执行的。这将提供一个小的加速,虽然由于管道效应。可能不值得麻烦

这个怎么样

这将只创建一个上下文,即,
将实体
附加到上下文。

如果插入过程中出现任何错误,则整个事务将回滚。如果您想要更多类似事务的模式实现

据我所知,
实体框架
本身具有工作单元模式

 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
        }

    }
公共存储配置(列表配置)
{
尝试
{
使用(var dc=new ConfigContext())
{
dc.Configuration.AutoDetectChangesEnabled=false;
foreach(configs中的var singleConfig)
{
//不要在循环中调用dc.SaveChanges。
//假设SaveConfiguration是您的表。
//这将把实体添加到DbSet,在调用SaveChanges之前不会插入到Db
dc.SaveConfiguration.Add(singleConfig);
} 
dc.Configuration.AutoDetectChangesEnabled=true;
dc.SaveChanges();
}
}
捕获(异常)
{
抛出异常
}
}

谢谢您的建议。我们办公室里没有专门的DB程序员,所以所有这些问题对我来说都是新问题。我猜交易范围是个骗局?在没有它的情况下,多个上下文调用似乎可以正常工作,但SQL Server一定是在幕后处理它们的。我将只使用一个连接运行一些测试,看看它是如何运行的。我最多只能插入10000行,所以我觉得处理时间>60秒是不合理的。不,涉及TransactionScope。没有它,变化就不是原子的,这是不正确的。有了它,更改是原子性的,但需要MSDTC(由于一些原因,最好避免使用MSDTC)。
我最多只插入约10000行,因此我觉得处理时间>60秒是不合理的。
如果您想要修复,请问一个新问题并在此处留下链接。使用SQL事件探查器捕获执行的SQL并发布实际执行计划。感谢您的建议!我将对工作单元模式进行一些研究,并尝试一下。
 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
        }

    }