c# - 添加了 ValidationContext 项的 MVC 和 EF 验证

标签 c# asp.net-mvc entity-framework validation

我有一个场景,我想将一个项目添加到 ValidationContext 并在 EF 触发的实体验证中检查它。我在向导中执行此操作,因此我只能在特定步骤中验证某些内容。 (如果有好的模式,请分享)。

问题是验证被触发了,实际上触发了两次,甚至在 Controller 操作被触发之前。我希望我明白为什么。我不确定如何在此之前获取 ValidationContext 中的项目,所以我无法告诉验证我正在进行哪个步骤。

此外,如果我只在通过检查项目触发保存更改时执行自定义验证,就像我在下面的代码中一样,那么在页面刷新时我不会看到自动模型验证错误。

在我的自定义上下文中:

public WizardStep Step { get; set; }

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    items.Add("ValidationStep", Step);
    return base.ValidateEntity(entityEntry, items);
}

设置实体的服务:

public void SaveChanges(WizardStep step)
{
    _context.Step = step;
    _context.SaveChanges();
}

在我的实体中

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    // Step will only be present when called from save changes.  Calls from model state validation won't have it
    if (validationContext.Items.ContainsKey("ValidationStep"))
    {
        var validationStep = (WizardStep)validationContext.Items["ValidationStep"];
        if (validationStep == WizardStep.Introduction)
        {
            if (criteria)
            {
                yield return new ValidationResult($"Error message  ", new[] { "field" });
            }
        }
    }
}

Controller :

public ActionResult MyAction(HomeViewModel vm)
{
    try
    {
        _incidentService.AddOrUpdate(vm.Enttiy);
        _incidentService.SaveChanges(WizardStep.Introduction);
    }
    catch (Exception ex)
    {
        return View(vm);
    }
    return RedirectToAction("Index");
}

最佳答案

第一个验证是在传递给 Controller ​​的 MVC 创建的模型上进行的。 MVC 使用 ModelBinder 类来构造、填充和验证客户端 http 表单数据到模型中。任何失败的验证都将返回给客户端。然后 Controller 可能会更改有效模型,因此在保存时由 EF 进行第二次验证。我相信保存后,只有当属性是新的或具有与原始值不同的数据时才会触发 EF 验证。

理论上应该可以有一个自定义的 MVC ModelValidator 并拦截 Validate 方法来设置 ValidationContext 项目。但是,我不知道该怎么做。然而,我确实找到了一个对我有用的稍微不同的解决方案。也许它可以根据您的需要进行调整。

在我的例子中,我希望 EF DbContext(在我的代码中名为 CmsEntities)可用于验证方法,以便我可以查询数据库(并进行丰富的复杂业务逻辑验证)。 Controller 具有 DbContext,但模型验证在将其传递给 Controller ​​的操作之前由 ModelBinder 调用。

我的解决方案是:

1) 将 DbContext 属性添加到我的实体(使用部分类,或在所有实体继承自的基础实体中)

2) 创建一个自定义 ModelBinder,它将从 Controller 获取 DbContext 并将其填充到模型

3) 在Application_Start()中注册自定义ModelBinder

现在,在任何验证方法中,模型都会有一个填充的 DbContext。 

自定义模型绑定(bind)器

public class CmsModelBinder : DefaultModelBinder
{
    protected override bool OnModelUpdating(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Copy CmsEntities from Controller to the Model (Before we update and validate the model)
        var modelPropertyInfo = bindingContext.Model.GetType().GetProperty("CmsEntities");
        if (modelPropertyInfo != null)
        {
            var controllerPropertyInfo = controllerContext.Controller.GetType().GetProperty("CmsEntities");
            if (controllerPropertyInfo != null)
            {
                CmsEntities cmsEntities = controllerPropertyInfo.GetValue(controllerContext.Controller) as CmsEntities;
                modelPropertyInfo.SetValue(bindingContext.Model, cmsEntities);
            }
        }            
        return base.OnModelUpdating(controllerContext, bindingContext);
    }

全局.asax.cs

    protected void Application_Start()
    {
        ...
        ModelBinders.Binders.DefaultBinder = new CmsModelBinder();
    }

关于c# - 添加了 ValidationContext 项的 MVC 和 EF 验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42773522/

相关文章:

c# - 空合并运算符是否在c#中缓存结果

c# - Unity LineRenderer - 无法设置位置

c# - 使用 MySQL 和 ASP.NET MVC 的池

asp.net-mvc - 在 asp.net mvc 中将文本框类型转换为密码

c# - 存储具有多个相同项目的集合

c# - 无锁引用计数

c# - 在 foreach 中访问 LINQ Select 中使用的值

asp.net-mvc - 种子角色(RoleManager 与 RoleStore)

c# - 团队环境 Entity Framework 代码优先 - 仅删除 _MigrationHistory 表以将模型项目重新同步到数据库是否合适?

c# - Entity Framework - 为什么选择导航属性?