foreach 循环中的实体验证,如果验证失败,则在下一个循环中保持失败

Posted

技术标签:

【中文标题】foreach 循环中的实体验证,如果验证失败,则在下一个循环中保持失败【英文标题】:Entity validation in foreach loop, if validation fails it stays failed for next loop 【发布时间】:2012-06-03 15:35:22 【问题描述】:

我遇到了一个奇怪的问题。我有一个包含现有项目的数据库,我添加了一个新的验证规则,因此数据库中的一些项目不符合这个新规则。

我有一个循环查找记录,更改元素然后将其保存回数据库,如下所示:

foreach(int foo in bar)

    Model model = db.Model.Find(foo);
    model.updated = true;
    if(ModelState.IsValid)
    
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
    

我认为对于不符合新验证规则的记录将不会更新,因为 ModelState.IsValid 不会通过。但情况并非如此,它会引发验证失败异常。所以我把它放在try catch 中,然后我想我会记录错误,这样我就知道哪些记录无效。所以它现在看起来像这样:

foreach(int foo in bar)

    Model model = db.Model.Find(foo);
    model.updated = true;
    try
    
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
    
    catch(Exception x)
    
        // log error
        if(ModelState.IsValid)
        
            db.ErrorLogs.Add(errorLog);
            db.SaveChanges();
        
    

这也会引发验证失败异常,我猜是因为try 中的异常没有被清除。很好,我决定尝试一下,而不是发现错误。所以它看起来像这样:

foreach(int foo in bar)

    Model model = db.Model.Find(foo);
    model.updated = true;
    try
    
        db.Entry(model).State = EntityState.Modified;
        db.SaveChanges();
    
    catch()
    
    

现在最终发生的事情是说我有 100 条记录在 foreach 循环中循环,如果 #27 验证失败后的每条记录都失败了,因此不会更新!

这很严重,我该如何解决?有没有办法清除验证错误?为什么错误持续到所有其他循环?是因为db 在循环外声明了吗? 为什么它首先通过 ModelState.IsValid?

谢谢

【问题讨论】:

你检查过for循环吗? 【参考方案1】:

我认为您将 ASP.NET MVC 验证与实体框架的验证混淆了。

ModelState.IsValid 检查绑定到 MVC 操作参数的模型的有效性。 实体框架会自行验证它尝试保存的实体。

您的模型中无效的方面似乎不是发送到您的操作方法的那些方面,而是来自数据库的那些方面。所以当模型绑定发生时,你有一个有效的对象,ModelState.IsValid 是真的。但是当您尝试保存对象时,Entity Framework 仍然会检测到它们无效并抛出异常。

您会在实体上创建导致数据库中当前数据不正确的验证,这似乎很奇怪。考虑运行 SQL 脚本来更正数据库中的数据。

如果您不能这样做,您可以在将实体加载出数据库后尝试绑定它们:

var models = db.Model.Where(f => bar.Contains(f.Id)).ToList();
TryUpdateModel(models);
if(ModelState.IsValid)

    foreach(var model in models)
    
        model.updated = true;
        db.Entry(model).State = EntityState.Modified;
    
    db.SaveChanges();

当然,上面的代码只有在模型的所有都有效的情况下才有效。如果您想选择性地只更新正确的条目,您可以检查ModelState.Errors 属性以了解与每个模型条目相关的错误。

如果您真的想在这种特殊情况下禁止实体验证,请记住这可能很危险,您可以在保存之前简单地禁用上下文验证。

db.Configuration.ValidateOnSaveEnabled = false;

【讨论】:

感谢您的信息,有没有办法改变实体的行为以不在此特定部分检查验证? @GarrettFogerlie:见***.com/questions/8099949/… 非常感谢,在保存更改之前添加 db.Configuration.ValidateOnSaveEnabled = false; 效果很好。将此添加到您的答案中,我会接受。我无法更新现有数据库信息的原因是因为它是一个是或否的答案,同意从一开始就应该要求的条款。谢谢!

以上是关于foreach 循环中的实体验证,如果验证失败,则在下一个循环中保持失败的主要内容,如果未能解决你的问题,请参考以下文章

如何在 foreach 循环中验证元素是不是在集合中的最后一个?

对一个或多个实体的验证失败。有关详细信息,请参见“EntityValidationErrors”属性。

RESTful 服务,如果验证失败如何响应?

如果身份验证失败,则关闭 HTTP 连接

验证通常失败时 JSF 中的条件样式

通过令牌进行身份验证,如果令牌失败,则通过会话进行身份验证,使用 Passport?