子属性的选择性validation – MVC中的流畅validation
我正在使用Fluent Validation与Ninject.Web.Mvc.FluentValidation库自动连接所有validation器(并使用dependency injection来创建validation器)。
我创建了以下模型:
public class Parent { public string Name { get; set; } public Child Child1 { get; set; } public Child Child2 { get; set; } } public class Child { public string ChildProperty { get; set; } }
使用以下validation器:
public class ParentValidator : AbstractValidator { public ParentValidator() { RuleFor(model => model.Name).NotEmpty(); RuleFor(model => model.Child1).SetValidator(new ChildValidator()); } } public class ChildValidator : AbstractValidator { public ChildValidator() { RuleFor(model => model.ChildProperty).NotEmpty(); } }
我的观点:
@model Parent @using(Html.BeginForm()) { @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) @Html.EditorFor(model => model.Child1) @Html.EditorFor(model => model.Child2) } @model Child @Html.EditorFor(model => model.ChildProperty) @Html.EditorFor(model => model.ChildProperty)
我想要实现的是拥有一个具有两个子属性的父模型。 Child1的财产是必需的,但Child2的财产是可选的。 这在正常情况下工作正常,但是当我使用Ninject模块自动连接validation器时,它会检测到Child类的validation器类型并连接Parent上的所有子属性。
有没有什么方法可以防止这种情况发生而不摆脱Ninject模块?
由于自动连接无法有条件地理解何时在模型绑定期间应用ChildValidator类,因此您似乎有以下几种选择:
- 确定重用子视图模型是否重要。 面对这种情况,如果Child对象不是很复杂并且没有多个视图分别使用Child对象,我可能会将子节点折叠为此视图的父节点。 我总是更不愿意使用视图模型进行超级DRY,因为根据我的经验,页面结构会随着时间的推移而发生分歧。
- 清除Child2的ModelState错误。 从这里开始,您可以完全控制Child2的validation,包括在此唯一上下文中单独使用Child2的validation器并手动应用它。 这是我喜欢FluentValidation的原因之一 – 能够在不同的上下文中将不同的validation逻辑应用于相同的视图模型,这与数据注释不同。
自动连线的价值(即它排除的所有额外代码)将排除在这种情况下将其关闭的选项,即IMO。
您忘记将validation器设置为第二个子属性:
public class ParentValidator : AbstractValidator { public ParentValidator() { RuleFor(model => model.Name).NotEmpty(); RuleFor(model => model.Child1).SetValidator(new ChildValidator()); RuleFor(model => model.Child2).SetValidator(new ChildValidator()); } }
如果您不想自动连接子validation器,可以向子validation器添加空接口:
公共类PersonalDataValidator:AbstractValidator,IChildValidator
然后在你的工厂:
public class FluentValidatorFactory : ValidatorFactoryBase { private readonly IKernel _kernel; public FluentValidatorFactory(IKernel kernel) { _kernel = kernel; } public override IValidator CreateInstance(Type validatorType) { IValidator validator = _kernel.Resolve(validatorType) as IValidator; ////we dont want that windosr auto wires up all child validators. var childValidator = validator as IChildValidator; if (childValidator == null) { return validator; } return null; } }