【问题标题】:Prevent the DbContext from repeatedly trying to save bad data防止 DbContext 反复尝试保存坏数据
【发布时间】:2014-07-22 23:51:08
【问题描述】:

我有一个进程正在导入 Excel 电子表格,并将数据解析到我的数据对象中。这些数据的来源非常值得怀疑,因为我们正在将我们的客户从基于电子表格的数据管理转移到一个可检查有效数据的托管数据库系统。

在我的导入过程中,我对数据进行了一些基本的完整性检查,以适应我们正在导入的数据可能有多糟糕,但我在 DbContext 中完成了我的整体验证。

我正在尝试做的部分事情是,我想在电子表格中提供数据错误的第 # 行,以便他们可以轻松确定需要修复什么来导入文件。

一旦我从电子表格 (model) 中获得数据,以及他们正在使用的数据库 (opp) 中的机会,这是我的流程的伪代码:

foreach (var model in Spreadsheet.Rows) { // Again, pseudocode
    if(opp != null && ValidateModel(model, opp, row)) {
        // Copy properties to the database object

        // This is in a Repository-layer method, not directly in my import process.
        // Just written here for clarity instead of several nested method calls.
        context.SaveChanges(); 
    }
}

如果需要,我可以在此处提供更多代码,但问题在于我的 DbContext 的 ValidateEntity() 方法(覆盖 DbContext)。

同样,据我所知,我编写的代码没有任何问题,但如果机会未能通过此级别的验证,那么它将作为未保存对象的一部分保留在 context 中,这意味着每次调用 ValidateEntity() 时它都会反复尝试进行验证。这会导致在初始问题发生后,每一行都会重复相同的验证错误消息。

有没有办法[编辑]让上下文在验证失败一次后停止尝试验证对象[编辑]?我知道我可以等到最后再打电话给context.SaveChanges() 一次来解决这个问题,但我希望能够将它与数据库中的行匹配。

作为参考,我正在使用 Entity Framework 6.1 和 Code First 方法。

编辑试图进一步澄清 Marc L.(包括对上述代码块的更新)

现在,我的流程将遍历电子表格中的行数。我之所以调用我的存储库层并保存每个对象,而不是使用只调用一次context.SaveChanges() 的方法,是为了让自己能够确定哪一行是导致验证错误的行。

我很高兴我的 DbContext 的自定义 ValidateEntity() 方法捕获了验证错误,但问题在于它没有为同一实体多次抛出 DbEntityValidationException

我希望如果对象验证失败一次,上下文不再尝试保存对象,无论context.SaveChanges() 被调用多少次。

【问题讨论】:

  • "有没有办法从context '分离'有问题的对象而不从数据库中删除?..." 也许我误解了,但如果对象足够有效以获得进入数据库,然后从DbContext 的角度来看它有效的。如果它在更高的业务对象角度无效,那是应该在其他地方处理的不同级别的验证,并且您的自定义 DbContext 缺乏适当的关注点分离......请澄清。
  • @Jimmy 我不认为这真的是我想要做的。也许“分离”是我试图使用的错误术语。
  • @MarcL。我会尽量澄清更多。

标签: c# entity-framework validation dbcontext validationerror


【解决方案1】:

您的问题不是骗人的(这是关于保存,而不是加载实体),但您可以按照上面 Jimmy 的建议进行操作。也就是说,一旦实体被添加到上下文中,它就会在“添加”状态下被跟踪,阻止它重新验证的唯一方法是分离它。这是一个 SO 内部链接,但我将重现代码 sn-p:

dbContext.Entry(entity).State = EntityState.Detached;

但是,我认为这不是您想要的方式,因为您使用异常来管理状态是不必要的(众所周知,异常的成本很高)。

根据给出的信息,我会使用更基于集合的解决方案:

  • 修改您的模型类,使其包含记录原始电子表格行的 RowID(也可能有其他充分的理由)
  • 关闭上下文的实体跟踪(变化检测允许每个 Add() 为 O(1))
  • 添加所有实体
  • 调用context.GetValidationErrors() 并立即获取所有错误,使用上述RowID 来识别无效行。

您没有指明您的进程是应该保存好行还是拒绝整个文件,但这可以满足任何一个要求 - 也就是说,如果您需要保存好行,请使用上面的代码,然后是SaveChanges()


最后,如果您确实想保存好的行并且您对基于集合的方法感到不舒服,最好为每一行使用一个新的DbContext,或者至少创建一个新的@987654326 @ 在每个错误之后。 ADO.NET 团队坚持认为上下文创建是“相对便宜的”(对不起,我手头没有引用或统计数据),所以这不会对您的吞吐量造成太大损害。即便如此,它至少会保持 O(n)。我不会责怪你,管理一个大的上下文也可以让你解决其他问题。

【讨论】:

  • 感谢您的回复。关于您上面所说的,我要澄清一点,SaveChanges() 将仅对位于AddedModifiedDeleted 状态的附加实体进行验证。所以在第一次成功保存后,它会被移动到Unchanged,并且不会再次被检查。如果每个对象都没有通过验证,那么是的,我同意你提到的 O(n^2) 缩放。
  • 我真的很想了解更多关于 DbContext 处置和创建新的“昂贵”的信息。我认为这比抛出异常更耗费资源。
  • 感谢您的帮助。很抱歉对正在发生的其他事情如此神秘,因为我认为它们不是真的必要,我不想再混淆这件事了。我正在批量保存所有内容,并结合了您在顶部建议的所有内容。正如我上面所说,我认为每个项目的新上下文都太昂贵而无法以这种方式实现。如果我能被证明是错的,那就太好了!
猜你喜欢
  • 1970-01-01
  • 2020-03-22
  • 2013-09-22
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多