有没有人知道在使用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");
}
}
答案 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。