防止DbContext反复尝试保存错误数据

时间:2014-06-02 19:19:35

标签: c# entity-framework validation dbcontext validationerror

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

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

我尝试做的部分原因是我想在电子表格中提供数据不好的Row#,以便他们可以轻松确定需要修复的内容以便导入文件。

一旦我获得了电子表格(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.(包括上述代码块的更新)

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

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

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

1 个答案:

答案 0 :(得分:1)

你的问题不是欺骗(这是关于保存,而不是加载实体),但你可以按照上面的吉米建议。也就是说,一旦将实体添加到上下文中,就会在"中添加"状态和阻止它重新验证的唯一方法是分离它。这是一个SO内部链接,但我将重现代码段:

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

但是,我并不认为这是您想要的方式,因为您不必要地使用例外来管理状态(例外情况非常昂贵)。

根据提供的信息,我使用更基于集合的解决方案:

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

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


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