【问题标题】:DBContext - remove pending changesDBContext - 删除挂起的更改
【发布时间】:2020-06-27 20:42:39
【问题描述】:

我有一个 MVC 应用程序,每个请求创建一次 DBContext。

在此请求中,我尝试保存订单列表

...

foreach (var order in orders)
{
  SaveOrder(order);
}

public SaveOrder(Order order)
{
  _context.Orders.Add(order);
  _context.SaveChanges();
}

...

但是,如果列表中的第一个订单抛出异常,那么其余的都不会保存。

我已经尝试捕获异常然后使用

  _context.Orders.Remove(order)

但在 DB 上下文中下一次调用 SaveChanges() 时,原始异常仍然会触发。

有些文章似乎建议为每个操作使用新的上下文,并在 using 语句中进行包装——但这并不昂贵(它不会每次都创建一个新连接吗?)

这样做的正确方法是什么?有没有办法删除所有待处理的更改?

谢谢

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    要处理要从上下文中删除项目的情况,您需要将其从 DbContext 中分离出来,而不是调用 RemoveRemove 可能会导致一些不良后果,例如删除现有行。

    所以在你的情况下,SaveOrder 调用应该通过这样做来工作:

    public void SaveOrder(Order order)
    {
        try
        {
            _context.Orders.Add(order);
            _context.SaveChanges();
        }
        catch (DbUpdateException ex)
        {
            _context.Entry(order).State = EntityState.Detached; // De-associate from the poisoned order.
            // TODO: Consider logging that this order could not be saved or returning a result to that effect.
        }
    }   
    

    请注意,如果 Order 实体具有作为此操作的一部分添加/更新的子项,您也需要分离这些子项。 IE。

        catch (DbUpdateException ex)
        {
            _context.Entry(order).State = EntityState.Detached; 
            foreach(var orderLine in order.OrderLines)
            {
                _context.Entry(orderLine).State = EntityState.Detached;
            }
        }
    

    仅适用于由操作添加或更新的行。如果此操作未创建/更新客户记录,您不会想要分离 Order.Customer。

    我更喜欢使用工作单元模式,它将插入新订单视为单个操作,包括一个工作单元。如果我创建 5 个订单,每个订单彼此独立(1 个失败不会影响其他订单),那么它们是 5 个独立的操作。启动作用域 DbContext 通常是避免交叉操作“中毒”的最安全选项,并且不会产生大量成本。对于接触相同数据的操作,会有额外的读取成本,尽管这些通常是按 ID 获取的场景(按 Id 的客户、按 Id 的产品等),因此它们的成本很低。过多地处理订单周围的附加和分离状态可能会导致更严重的问题,因为在 Save 调用之后,“订单”引用可能现在也可能不是被跟踪的实体。

    【讨论】:

      【解决方案2】:

      这可能会有所帮助

      public void SaveOrders()
      {
          try
          {
              foreach (var order in orders)
              {
                  if (!IsOrderEntryValid(order))
                  {
                      _invalidOrders.Add(order);
                      continue;
                  }
                  if(!_context.Orders.Exists(/* write your logic to check if the order exists*/))
                      _context.Orders.Add(order);
      
              }
              _context.SaveChanges();
          }
          catch(Exception ex)
          {
              // log the exception
          }
      }
      
      public ValidateOrder(Order order)
      {
          // do some validations here that would not violate the data format and rules, cause violations and exceptions.
      }
      

      由于问题中的给定信息,这是非常通用的。我敢肯定,你会想办法验证并确保保存数据不会因为数据库违规、约束违规、重复等而引发异常。你会从它抛出的异常中学习和/或必须记录它。对我来说,一个不断发展的系统涉及学习平台及其与数据相关的行为。

      【讨论】:

      • 好主意,正如您所说,这需要知道在验证中要查找的内容,而不是捕获任何未处理的错误。对于这个特定的错误,我已经进行了修复以修复传入的数据。仍然对处理意外异常的最佳方法持开放态度
      猜你喜欢
      • 2016-04-20
      • 2013-12-31
      • 2021-12-26
      • 2012-06-07
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 2020-09-23
      • 2016-11-17
      相关资源
      最近更新 更多