防止DbContext反复尝试保存坏数据

本文关键字:保存 数据 DbContext 防止 | 更新日期: 2023-09-27 18:06:22

我有一个导入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()时,它都会反复尝试验证。这将导致在初始问题发生后,对每一行重复相同的Validation Error消息。

是否有一种方法可以[edit]让上下文在验证失败后停止尝试验证对象一次[edit]?我知道我可以等到结束并在结束时调用context.SaveChanges()一次来解决这个问题,但我希望能够将其与数据库中的行相匹配。

作为参考,我使用实体框架6.1与代码优先的方法。

EDIT试图为Marc L.进一步澄清(包括对上面代码块的更新)

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

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

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

防止DbContext反复尝试保存坏数据

你的问题不是骗人的(这是关于保存,而不是加载实体),但你可以遵循上面Jimmy的建议。也就是说,一旦一个实体被添加到上下文中,它就会在"添加"状态下被跟踪,而阻止它重新验证的唯一方法就是分离它。这是一个SO-internal链接,但我将重新生成代码片段:

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

然而,我不认为这是你想要的方式,因为你使用异常来管理不必要的状态(异常是出了名的昂贵)。

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

  • 修改你的模型类,使它包含一个记录原始电子表格行的RowID(也可能有其他很好的理由)
  • 关闭上下文的实体跟踪(允许每个Add()为0(1)的更改检测回合)
  • 添加所有实体
  • 调用context.GetValidationErrors()并立即获得所有错误,使用前面提到的RowID来识别无效行。

您没有指出您的进程是应该保存好的行还是拒绝整个文件,但是这将适应两者——也就是说,如果您需要保存好的行,使用上面的代码分离所有无效的行,然后使用SaveChanges()


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