【问题标题】:Declarative conditional validation of a field based on other one from parent model基于来自父模型的另一个字段的声明性条件验证
【发布时间】:2013-08-16 12:23:34
【问题描述】:

我有两种不同的类型:

public class Person
{
    public string Name { get; set; }
    public bool IsActive { get; set; }

    public Contact ContactDetails { get; set; }
}

public class Contact
{
    [RequiredIfActive]
    public string Email { get; set; }
}

我需要的是根据父模型字段的某些状态对内部模型字段执行条件声明性验证 - 在此特定示例中,如果启用了 IsActive 选项,则必须填写 Email

我不想重新组织这些模型分类,同时我需要使用基于属性的方法。似乎从属性中无法访问父模型的验证上下文。如何到达或注入那里?

public class RequiredIfActiveAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, 
                                                ValidationContext validationContext)
    {
        /* validationContext.ObjectInstance gives access to the current 
           Contact type, but is there any way of accessing Person type? */

编辑:

我知道如何使用 Fluent Validation 实现条件验证,但我不是在问这个问题(我不需要 Fluent Validation 方面的支持)。但是我想知道,是否存在从System.ComponentModel.DataAnnotations.ValidationAttribute 内部访问父模型的任何方法。

【问题讨论】:

  • Fluent 验证比默认的 MVC 验证要好得多。在使用默认 mvc 验证 4 年后,我在 3 个月前切换到 fluent 验证。
  • @Softlion:这个问题主要是我的好奇心,我不想验证任何东西。在这种特殊情况下,我也不想简化我的生活。这就是为什么 Fluent Validation 建议在任何层面都不会针对我的问题。

标签: .net asp.net-mvc data-annotations asp.net-mvc-validation system.componentmodel


【解决方案1】:

我的建议

转到 Tools => Library Package Manager => Package Manager Console 并安装 Fluent Validation。

操作方法

[HttpGet]
public ActionResult Index()
{
    var model = new Person
    {
        Name = "PKKG",
        IsActive = true,
        ContactDetails = new Contact { Email = "PKKG@stackoverflow.com" }
    };
    return View(model);
}
[HttpPost]
public ActionResult Index(Person p)
{
    return View(p);
}

流畅的验证规则

public class MyPersonModelValidator : AbstractValidator<Person>
{
    public MyPersonModelValidator()
    {
        RuleFor(x => x.ContactDetails.Email)
            .EmailAddress()
            .WithMessage("Please enter valid email address")
            .NotNull().When(i => i.IsActive)
            .WithMessage("Please enter email");
    }
}

查看模型

[Validator(typeof(MyPersonModelValidator))]
public class Person
{
    [Display(Name = "Name")]
    public string Name { get; set; }

    [Display(Name = "IsActive")]
    public bool IsActive { get; set; }

    public Contact ContactDetails { get; set; }
}

public class Contact
{
    [Display(Name = "Email")]
    public string Email { get; set; }
}

查看

@{
    var actionURL = Url.Action("Action", "Controller", new { area = "AreaName" },
                           Request.Url.Scheme);
}
@using (Html.BeginForm("Action", "Controller", FormMethod.Post, 
                                                    new { @action = actionURL }))
    @Html.EditorFor(i => i.Name);
    @Html.ValidationMessageFor(i => i.Name);

    @Html.EditorFor(i => i.IsActive);
    @Html.ValidationMessageFor(i => i.IsActive);

    @Html.EditorFor(i => i.ContactDetails.Email);
    @Html.ValidationMessageFor(i => i.ContactDetails.Email);
    <button type="submit">
        OK</button>
}

【讨论】:

  • 感谢您的努力,但由于问题本身(以及现在已删除的 cmets)中已明确注释,因此流利验证不是我在特定情况下寻找的解决方案案例。
【解决方案2】:

这不能通过Contact.Email 上的属性来完成,因为正如您已经发现的那样,父Person 在运行时无法从属性上下文中获得。要通过验证属性启用此场景,该属性必须装饰 Person 类。对于 System.ComponentModel.DataAnnotations 属性,您有两种选择:CustomValidationAttribute 或以 Person 为目标的自定义 ValidationAttribute 子类。

这是使用CustomValidationAttribute 时这两个类可能的样子:

[CustomValidation(typeof(Person), "ValidateContactEmail")]
public class Person
{
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public Contact ContactDetails { get; set; }

    public static ValidationResult ValidateContactEmail(Person person, ValidationContext context)
    {
        var result = ValidationResult.Success;
        if (person.IsActive)
        {
            if ((person.ContactDetails == null) || string.IsNullOrEmpty(person.ContactDetails.Email))
            {
                result = new ValidationResult("An e-mail address must be provided for an active person.", new string[] { "ContactDetails.Email" });
            }
        }

        return result;
    }
}

public class Contact
{
    public string Email { get; set; }
}

【讨论】:

  • 如果可能的话,您能否抽出一些时间提供一个代码示例以获取更多详细信息?
  • 我为 CustomValidationAttribute 添加了一个示例。您是否也需要一个自定义 ValidationAttribute 子类?
猜你喜欢
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 2012-06-23
  • 1970-01-01
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多