3

I have two separate types:

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

What I need is to perform conditional declarative validation of internal model field, based on some state of the parent model field - in this particular example an Email has to be filled, if IsActive option is enabled.

I do not want to reorganize these models taxonomy, while in the same time I need to use attribute-based approach. It seems that from within an attribute there is no access to the validation context of the parent model. How to reach or inject it there?

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? */

Edit:

I know how conditional validation can be implemented using Fluent Validation, but I'm NOT asking about that (I don't need support regarding Fluent Validation). I'd like to know however, if exists any way to access parent model from inside System.ComponentModel.DataAnnotations.ValidationAttribute.

4

2 回答 2

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

Fluent 验证规则

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>
}
于 2013-09-07T17:57:58.013 回答
1

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

以下是这两个类在使用时的样子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; }
}
于 2013-09-10T14:02:27.277 回答