Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/254.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Validation 如何强制MVC验证IValidatableObject_Validation_Asp.net Mvc 3 - Fatal编程技术网

Validation 如何强制MVC验证IValidatableObject

Validation 如何强制MVC验证IValidatableObject,validation,asp.net-mvc-3,Validation,Asp.net Mvc 3,看起来,当MVC验证一个模型时,它首先通过DataAnnotation属性(如required或range)运行,如果其中任何属性失败,它将跳过在我的IValidatableObject模型上运行Validate方法 有没有办法让MVC继续运行该方法,即使其他验证失败?您可以通过传入ValidationContext的新实例手动调用Validate(),如下所示: [HttpPost] public ActionResult Create(Model model) { if (!Mode

看起来,当MVC验证一个模型时,它首先通过DataAnnotation属性(如required或range)运行,如果其中任何属性失败,它将跳过在我的IValidatableObject模型上运行Validate方法


有没有办法让MVC继续运行该方法,即使其他验证失败?

您可以通过传入ValidationContext的新实例手动调用Validate(),如下所示:

[HttpPost]
public ActionResult Create(Model model) {
    if (!ModelState.IsValid) {
        var errors = model.Validate(new ValidationContext(model, null, null));
        foreach (var error in errors)                                 
            foreach (var memberName in error.MemberNames)
                ModelState.AddModelError(memberName, error.ErrorMessage);

        return View(post);
    }
}
这种方法的一个警告是,在没有属性级别(DataAnnotation)错误的实例中,验证将运行两次。为了避免这种情况,您可以向模型中添加一个属性,比如一个boolean Validated,在Validate()方法运行后将其设置为true,然后在控制器中手动调用该方法之前进行检查

因此,在控制器中:

if (!ModelState.IsValid) {
    if (!model.Validated) {
        var validationResults = model.Validate(new ValidationContext(model, null, null));
        foreach (var error in validationResults)
            foreach (var memberName in error.MemberNames)
                ModelState.AddModelError(memberName, error.ErrorMessage);
    }

    return View(post);
}
在您的模型中:

public bool Validated { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
    // perform validation

    Validated = true;
}
public bool验证{get;set;}
公共IEnumerable验证(ValidationContext ValidationContext){
//执行验证
已验证=正确;
}

有一种方法可以做到这一点,而无需在每个控制器操作的顶部添加样板代码

您需要将默认模型活页夹替换为您自己的:

protected void Application_Start()
{
    // ...
    ModelBinderProviders.BinderProviders.Clear();
    ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider());
    // ...
}
您的模型绑定器提供程序如下所示:

public class CustomModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(Type modelType)
    {
        return new CustomModelBinder();
    }
}
现在,创建一个自定义模型绑定器,该绑定器实际上强制进行验证。这是完成重物搬运的地方:

public class CustomModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        base.OnModelUpdated(controllerContext, bindingContext);

        ForceModelValidation(bindingContext);
    }

    private static void ForceModelValidation(ModelBindingContext bindingContext)
    {
        var model = bindingContext.Model as IValidatableObject;
        if (model == null) return;

        var modelState = bindingContext.ModelState;

        var errors = model.Validate(new ValidationContext(model, null, null));
        foreach (var error in errors)
        {
            foreach (var memberName in error.MemberNames)
            {
                // Only add errors that haven't already been added.
                // (This can happen if the model's Validate(...) method is called more than once, which will happen when
                // there are no property-level validation failures.)
                var memberNameClone = memberName;
                var idx = modelState.Keys.IndexOf(k => k == memberNameClone);
                if (idx < 0) continue;
                if (modelState.Values.ToArray()[idx].Errors.Any()) continue;

                modelState.AddModelError(memberName, error.ErrorMessage);
            }
        }
    }
}
公共类CustomModelBinder:DefaultModelBinder
{
模型更新时受保护的覆盖无效(ControllerContext ControllerContext、ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext、bindingContext);
ForceModelValidation(bindingContext);
}
私有静态void ForceModelValidation(ModelBindingContext bindingContext)
{
var model=bindingContext.model作为IValidatableObject;
if(model==null)返回;
var modelState=bindingContext.modelState;
var errors=model.Validate(新的ValidationContext(model,null,null));
foreach(错误中的var错误)
{
foreach(变量memberName出错。memberName)
{
//仅添加尚未添加的错误。
//(如果多次调用模型的Validate(…)方法,则可能会发生这种情况,这将在
//没有属性级验证失败。)
var memberNameClone=memberName;
var idx=modelState.Keys.IndexOf(k=>k==memberNameClone);
如果(idx<0)继续;
如果(modelState.Values.ToArray()[idx].Errors.Any())继续;
AddModelError(memberName、error.ErrorMessage);
}
}
}
}
您还需要一个IndexOf扩展方法。这是一个便宜的实现,但它可以工作:

public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    if (source == null) throw new ArgumentNullException("source");
    if (predicate == null) throw new ArgumentNullException("predicate");

    var i = 0;
    foreach (var item in source)
    {
        if (predicate(item)) return i;
        i++;
    }

    return -1;
}
public static int IndexOf(此IEnumerable源,Func谓词)
{
如果(source==null)抛出新的ArgumentNullException(“source”);
如果(谓词==null)抛出新的ArgumentNullException(“谓词”);
var i=0;
foreach(源中的var项)
{
if(谓语(项))返回i;
i++;
}
返回-1;
}

老实说,我开始喜欢这种默认行为了。如果您在Validate方法中执行业务级验证,该方法涉及昂贵的东西,如数据库连接,那么最好不要调用它们,除非模型有效。谢谢,这对我帮助很大。我认为此解决方案仅适用于DefaultModelBinder感谢它的工作。但我有一个问题,这有什么原因吗,因为Ivalidate物体在开火。有时可能不是吗?