Pass context to collection validators in FluentValidation with MVC integration

asp.net-mvc, c#, fluentvalidation, ninject

Solution

I found a way I can do it. See the below:

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;
    }
}

Using a Validator Interceptor and applying the rules in the BeforeMvcValidation method instead of in the constructor. Also then passing the validation context instance to the BarValidator.

Problem

does anyone know how I could pass a context for one validator to a collection validator while using the MVC4 integration of FluentValidation and the Ninject Validator Factory integration? Example: ``` 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() */; } } ``` when I am using this 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; } } ``` and adding the relevant validator factory to the MVC ModelValidatorProviders and binding them. ``` ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(new NinjectValidatorFactory(kernel))); DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; AssemblyScanner.FindValidatorsInAssemblyContaining<FooValidator>() .ForEach(match => kernel.Bind(match.InterfaceType).To(match.ValidatorType).InRequestScope()); ``` so it eventually can be used like this in the controller, so I don't have to new up a validator each time.. but I need to also pass the relevant context instance at the time of validating. ``` 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"); } } ```

Original source