使用MVC集成将上下文传递给FluentValidation中的集合验证器

时间:2013-04-06 17:34:37

标签: c# asp.net-mvc ninject fluentvalidation

有没有人知道在使用FluentValidation和Ninject Validator Factory集成的MVC4集成时如何将一个验证器的上下文传递给集合验证器?

示例:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; } 
}

public class Bar
{
    public string Bizz { get; set; }
}

public class FooValidator : AbstractValidator<Foo>
{

    public FooValidator()
    {
        // TODO: send context instance (Foo object) to each BarValidator?
        RuleFor(f => f.Bars).SetCollectionValidator(new BarValidator(/* my context instance here */ ));
    }
}

public class BarValidator : AbstractValidator<Bar>
{
    private IEnumerable<Bar> BarList { get; set; }

    public BarValidator(){ /* this is what the validator factory currently uses but i want to use the one that passes the fooInstance some how */ }

    public BarValidator(Foo fooInstance)
    {
        BarList = fooInstance.Bars.Where(b => b != null).ToList();

        // some function here to validate the Bizz property based on other Bar objects in the BarList collection
        RuleFor(f => f.Bizz).NotEmpty()/* .SomeBizzSiblingFunction() */;

    }
}

当我使用这个Ninject Validator Factory时

public class NinjectValidatorFactory : ValidatorFactoryBase
{
    /// <summary>
    /// Initializes a new instance of the <see cref="NinjectValidatorFactory"/> class.
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    public NinjectValidatorFactory(IKernel kernel)
    {
        Kernel = kernel;
    }

    /// <summary>
    /// Gets or sets the kernel.
    /// </summary>
    /// <value>The kernel.</value>
    public IKernel Kernel { get; set; }

    /// <summary>
    /// Creates an instance of a validator with the given type using ninject.
    /// </summary>
    /// <param name="validatorType">Type of the validator.</param>
    /// <returns>The newly created validator</returns>
    public override IValidator CreateInstance(Type validatorType)
    {
        if (((IList<IBinding>)Kernel.GetBindings(validatorType)).Count == 0)
        {
            return null;
        }

        return Kernel.Get(validatorType) as IValidator;
    }
}

并将相关的验证器工厂添加到MVC ModelValidatorProviders并绑定它们。

ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new NinjectValidatorFactory(kernel)));

DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;

AssemblyScanner.FindValidatorsInAssemblyContaining<FooValidator>()
                            .ForEach(match => kernel.Bind(match.InterfaceType).To(match.ValidatorType).InRequestScope());

所以它最终可以在控制器中这样使用,所以我不必每次都新建一个验证器..但是我还需要在验证时传递相关的上下文实例。

public class FooController : Controller {
    public ActionResult Create() {
        return View();
    }

    [HttpPost]
    public ActionResult Create(Foo foo) {

        if(! ModelState.IsValid) { // re-render the view when validation failed.
            return View("Create", foo);
        }

        return RedirectToAction("Index");

    }
}

1 个答案:

答案 0 :(得分:3)

我找到了一种方法可以做到。见下文:

public class FooValidator : AbstractValidator<Foo>, IValidatorInterceptor   
{

    public FooValidator() { }

    public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
    {
        RuleFor(f => f.Bar).SetCollectionValidator(new BarValidator(validationContext.InstanceToValidate as Foo));

        return validationContext;
    }

    public ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result)
    {
        return result;
    }
}

使用Validator Interceptor并在BeforeMvcValidation方法中应用规则,而不是在构造函数中应用规则。然后将验证上下文实例传递给BarValidator。