我有一个场景,我想将一个项目添加到 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/