基于父模型中的其他字段的字段的声明性条件验证

时间:2013-08-16 12:23:35

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

我有两种不同的类型:

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

我需要的是根据父模型字段的某些状态执行内部模型字段的条件声明性验证 - 在此特定示例中,如果Email选项为IsActive,则必须填充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? */ 启用。

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

System.ComponentModel.DataAnnotations.ValidationAttribute

修改

我知道如何使用Fluent验证来实现条件验证,但我不是在询问(我不需要有关Fluent验证的支持)。但是,我想知道,如果存在从{{1}}内部访问父模型的任何方式。

2 个答案:

答案 0 :(得分:1)

我的建议

转到工具=>库包管理器=>包管理器控制台并安装Fluent验证。

enter image description here

行动方法

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

答案 1 :(得分:1)

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

以下是使用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; }
}