我有一个场景,我希望向ValidationContext中添加一个项,并在EF触发的实体验证中检查它。我是在向导中这样做的,所以我只能在特定的步骤上验证某些事情。(如果有一个好的模式,请分享)。
问题是验证是在控制器操作被击中之前触发的,实际上是两次。我希望我能理解为什么。在这种情况发生之前,我不知道如何在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" });
}
}
}
}
主计长:
public ActionResult MyAction(HomeViewModel vm)
{
try
{
_incidentService.AddOrUpdate(vm.Enttiy);
_incidentService.SaveChanges(WizardStep.Introduction);
}
catch (Exception ex)
{
return View(vm);
}
return RedirectToAction("Index");
}
发布于 2017-03-22 05:22:33
第一个验证是在传递给控制器的MVC创建模型上进行的。MVC使用一个ModelBinder类来构造、填充和验证客户端http表单数据到模型中。任何失败的验证都将返回给客户端。然后,控制器可以更改有效的模型,因此保存时由EF进行第二次验证。我相信保存时,只有当属性是新的或有不同数据的原始值时,才会触发EF验证。
从理论上讲,应该有一个定制的MVC ModelValidator并拦截验证方法来设置ValidationContext项。但是,我想不出该怎么做。然而,我确实找到了一个对我有用的稍微不同的解决方案。也许它可以适应你的需要。
在我的例子中,我希望EF DbContext (在我的代码中命名为CmsEntities)可以用于验证方法,这样我就可以查询数据库(并进行丰富的复杂业务逻辑验证)。控制器具有DbContext,但是模型验证在传递给控制器操作之前由ModelBinder调用。
我的解决办法是:
1)向我的实体添加一个DbContext属性(使用部分类,或者在所有实体继承的基本实体中)
2)创建一个自定义ModelBinder,从控制器获取DbContext并将其填充到模型中
3)在ModelBinder ()中注册自定义ModelBinder
现在,在任何验证方法中,模型都有一个填充的DbContext。
自定义ModelBinder
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);
}
Global.asax.cs
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new CmsModelBinder();
}
发布于 2017-03-22 15:32:40
首先,您应该考虑WizardStep是否属于上下文或在单独的步骤中被修改的对象?另一件事是为什么不使用ie。在不同步骤上处理验证逻辑的策略?
关于验证,我看到你混合了两件事。
一个是对上下文的验证,在这里您应该处理上下文中的每个实体类型的验证逻辑。
二是IValidatableObject.Validate的实现,在SaveChanges上自动调用实体。
我会决定并选择一种方法,从你给我们的信息来看,我认为只有IValidatableObject.Validate更有意义,但是你要么将步骤放入正在验证的实体中,要么以某种方式将该步骤注入其他方法以进行验证。
发布于 2017-03-23 00:20:45
你可以这样做:
try
{
//write code
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
var outputLines = new List<string>();
foreach (var eve in ex.EntityValidationErrors)
{
outputLines.Add(string.Format(
"{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:",
DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State));
foreach (var ve in eve.ValidationErrors)
{
outputLines.Add(string.Format(
"- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage));
}
}
System.IO.File.AppendAllLines(@"c:\temp\errors.txt", outputLines);
}
https://stackoverflow.com/questions/42773522
复制相似问题