子属性的选择性验证 - MVC 中的 Fluent Validation

发布于 2024-12-28 20:20:49 字数 1430 浏览 3 评论 0原文

我将 Fluent Validation 与 Ninject.Web.Mvc.FluentValidation 库结合使用来自动连接所有验证器(并使用依赖项注入来创建验证器)。

我创建了以下模型:

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

使用以下验证器:

public class ParentValidator : AbstractValidator<Parent>
{
    public ParentValidator()
    {
         RuleFor(model => model.Name).NotEmpty();
         RuleFor(model => model.Child1).SetValidator(new ChildValidator());
    }
}

public class ChildValidator : AbstractValidator<Child>
{
    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)

    <input type="submit" value="Save" />
}

@model Child

@Html.EditorFor(model => model.ChildProperty)
@Html.EditorFor(model => model.ChildProperty)

我想要完成的是拥有一个具有两个子属性的父模型。 Child1 的属性是必需的,但 Child2 的属性是可选的。这在正常情况下工作得很好,但是当我使用 Ninject 模块自动连接验证器时,它会检测到 Child 类有一个验证器类型,并连接 Parent 上的所有 Child 属性。

有什么方法可以在不删除 Ninject 模块的情况下防止这种情况发生吗?

I'm using Fluent Validation with the Ninject.Web.Mvc.FluentValidation library to automatically wire up all of my validators (and use dependency injection to create the validators).

I created the following Models:

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

With the following validators:

public class ParentValidator : AbstractValidator<Parent>
{
    public ParentValidator()
    {
         RuleFor(model => model.Name).NotEmpty();
         RuleFor(model => model.Child1).SetValidator(new ChildValidator());
    }
}

public class ChildValidator : AbstractValidator<Child>
{
    public ChildValidator()
    {
        RuleFor(model => model.ChildProperty).NotEmpty();
    }
}

My Views:

@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)

    <input type="submit" value="Save" />
}

@model Child

@Html.EditorFor(model => model.ChildProperty)
@Html.EditorFor(model => model.ChildProperty)

What I am trying to accomplish is to have a parent model that has two child properties. Child1's property is required but Child2's property is optional. This works fine under normal circumstances, but when I use the Ninject module to wire up the validators automatically, then it is detecting that there is a validator type for the Child class and wiring up all of the Child properties on the Parent.

Is there any way I can prevent this from happening without getting rid of the Ninject module?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

爱要勇敢去追 2025-01-04 20:20:49

由于自动连接无法有条件地了解何时在模型绑定期间应用 ChildValidator 类,因此您似乎有几种选择:

  1. 确定子视图模型的重用是否那么重要。
  2. 清除 Child2 的 ModelState 错误。 从这里,您可以完成控制 Child2 的验证,包括在这个独特的上下文中为 Child2 提供一个单独的验证器并手动应用它。这是我喜欢 FluentValidation 的原因之一 - 与数据注释不同,它能够在不同上下文中将不同的验证逻辑应用于相同的视图模型。

在我看来,自动连接的价值(即它排除的所有额外代码)将排除在这种情况下关闭它的选项。

Since the auto-wireup wouldn't have a way to conditionally understand when to apply the ChildValidator class during model binding, it seems like you have a few alternatives:

  1. Decide if reuse of the child view models is that important. Faced with this situation, I would probably collapse the children into the parent for this view if the Child objects weren't very complex and there weren't more than a couple of views that used Child objects separately. I'm always a bit more reluctant to be super-DRY with view models, since page structures tend to diverge over time in my experience.
  2. Clear ModelState errors for Child2. From here, you could take complete control of validation for Child2, including a separate validator for Child2 altogether in this unique context and applying it manually. Which is one of the reasons I love FluentValidation - the ability to apply different validation logic to the same view model in different contexts, unlike data annotations.

The value of auto-wireup (i.e., all the extra code it precludes) would rule out the option of turning that off for this one case, IMO.

冬天旳寂寞 2025-01-04 20:20:49

您忘记将验证器设置为第二个子属性:

public class ParentValidator : AbstractValidator<Parent>
{
    public ParentValidator()
    {
         RuleFor(model => model.Name).NotEmpty();
         RuleFor(model => model.Child1).SetValidator(new ChildValidator());
         RuleFor(model => model.Child2).SetValidator(new ChildValidator());
    }
}

You forget to set validator to second child property:

public class ParentValidator : AbstractValidator<Parent>
{
    public ParentValidator()
    {
         RuleFor(model => model.Name).NotEmpty();
         RuleFor(model => model.Child1).SetValidator(new ChildValidator());
         RuleFor(model => model.Child2).SetValidator(new ChildValidator());
    }
}
套路撩心 2025-01-04 20:20:49

如果您不想自动连接子验证器,您可以向子验证器添加空接口:

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

If you don't want to auto wireup child validators you can add empty interface to child validator:

public class PersonalDataValidator : AbstractValidator, IChildValidator

And then in your factory:

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文