代码之家  ›  专栏  ›  技术社区  ›  markzzz

SaveChanges()不是由事务管理的吗?

  •  0
  • markzzz  · 技术社区  · 3 年前

    我有几个“线程”(即Hangfire任务)将并行运行这种函数(每个调用传递不同的 systemId ):

    private void UpdatePartners(int systemId)
    {
        try
        {
            using (var ctx = new MyEntities())
            {
                var partners = ctx.PartnersUpdate.Select(s => s)
                  .Where(w => w.SystemId.Equals(systemId) && statusToEvaluate.Contains(w.Status)).OrderByDescending(p => p.UpdateAt).Take(500);
    
                var partnersId = partners.Select(p => p.PartnerId).ToList();
    
                var partnersToUpdate = ctx.vPartnersSystems.AsNoTracking().Select(s => s)
                        .Where(w => w.SystemId.Equals(systemId) && partnersId.Contains(w.Id) && w.Validity.Equals(true))
                        .ToList();
    
                foreach (var partner in partners)
                {
                    // some operations on "partner", using data from "partnersToUpdate"
                    
                    ctx.PartnersUpdate.AddOrUpdate(partner);
                }
    
                ctx.SaveChanges();
            }
        }
        catch (Exception ex)
        {
            Logger(ex, "Error on UpdatePartners");
        }
    }
    

    不幸的是,有时我会遇到这个错误:

    UpdatePartners(Int32 systemId) in C:\myProjects\XYZ\Controllers\TestController.cs:line 16 Void HandleReaderException(System.Exception)
    System.Data.SqlClient.SqlException (0x80131904): Transaction (Process ID 130) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
    

    当迭代合作伙伴时(但我可能错了):

    foreach (var partner in partners)
    

    为什么?如果 UpdatePartners() 与不同的并行调用 系统ID (这意味着每次查询都会捕获 不同的记录 ),为什么它处理“死锁”?

    “相同行”的死锁不是被读取/更新了吗?

    0 回复  |  直到 3 年前
        1
  •  1
  •   Steve Py    3 年前

    一个Contoso实例与一个事务一起操作,但是如果你有多个任务在运行,每个任务都访问对同一表操作的Contoso实例,你可能会遇到死锁。

    我看到的一个问题是,如果你正在根据替代来源检查合作伙伴并进行更改,你不应该调用AddOrUpdate。合作伙伴已经包含被跟踪的实例,因此只需调用 SaveChanges() .AddOrUpdate实际上只用于种子数据库。

    在与其他数据操作并行运行此类批量操作时,您需要非常小心。一次加载的实体越多,EF执行读取和保存的时间就越长,死锁的窗口就越大。一次点击处理500条记录是相当少的。如果此任务不断运行,检查和更新行,您可能需要减少这一点,如果没有行要处理,也可以让它退出,而无需进一步查询。

    如果这是SQL Server,您可能还需要考虑检查数据库是否为读取提交的快照设置。这可以通过利用行版本控制来处理并发性来帮助防止死锁情况。

        2
  •  -1
  •   Dharman Aman Gojariya    3 年前

    正如你提到的,你有时会使用 partnersToUpdate ,您应该删除 AsNoTracking() 选项,因为实体框架失去了对实体的跟踪,无法更新它们