开发者

MVC 3 Conditionally Required Properties

How do you create conditionally required properties with the MVC 3 framework that will work with Client Side Validation as well as Server Side Validation when JS is disabled? For example:

public class PersonModel
{
  [Required] // Requried if Location is not set
  public string Name {get; set;}
  [Range( 1, 5 )] // Requried if Location is not set
  public int Age {get; set;}

  [Required] // Only required if Name and Age are not set.
  public string Location {get; set;}
}

The rules in this example are:

  1. Name and Age are required if Location is not set.
  2. Location is required only if Name and Age are not set.
  3. Doesn't matter if Name, Age and Location are all set.

In the View, I need the result sent to an Action if Name/Age are set. And a different Action if Location is set. I've tried with 2 separate forms with different Get Url's; this works great except the validation rules are causing problems. Preferrably, I would like the use of 2 separate Get action Url's, i.e.,

@model PersonModel

@using( Html.BeginForm( "Age", "Person", FormMethod.Post ) )
{
  @Html.TextBoxFor( x => x.Name )
  @Html.ValidationMessageFor( x => x.Name )
  @Html.TextBoxFor( x => x.Age )
  @Html.ValidationMessageFor( x => x.Age )
  <input type="submit" value="Submit by Age" />
}

@using( Html.BeginForm( "Location", "Person", FormMethod.Post ) )
{
  @Html.TextBoxFor( x => x.Location )
  @Html.ValidationMessageFor( x => x.Location )
  &开发者_运维百科lt;input type="submit" value="Submit by Location" />
}

Based on the PersonModel above, if the Location is submitted, the validation will fail since the PersonModel is expecting the Name and Age to be set as well. And vice versa with Name/Age.

Given the above mocked up example, how do you create conditionally required properties with the MVC 3 framework that will work with Client Side Validation as well as Server Side Validation when JS is disabled?


You can add custom validation to your model either subclassing ValidationAttribute or implementing IValidatableObject.

The ValidationAttribute allows you to add client side validation relatively simply by implementing IClientValidatable and registering a new adapter and method via jQuery. See Perform client side validation for custom attribute.

IValidatableObject is more suited to one-off validation requirements where reuse is not an option. It also results in slighlty simpler code. Unfortunately, there is no easy way to implement client side validation using this method.


I created my own RequiredAttribute descendant. It accepts a boolean property name, what should be the condition of the validation. Note, that this code is not production ready, lacks error checking and one could do some refining on checking for null.

[Localizable(false),AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RequiredIfAttribute : RequiredAttribute
{
    public string BoolProperty { get; private set; }
    public RequiredIfAttribute(string boolProperty)
    {
        BoolProperty = boolProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (!Equals(value, null) || !string.IsNullOrEmpty(value as string))
            return ValidationResult.Success;
        var boolProperty = validationContext.ObjectInstance.GetType().GetProperty(BoolProperty);
        var boolValue = (bool)boolProperty.GetValue(validationContext.ObjectInstance, null);
        if (!boolValue)
            return ValidationResult.Success;
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }
}

You could create a readonly property to represent your condition like below. Also note, that the Age property cannot be "empty" in your code. If you'd like to support it, you should use a nullable int (int?) type for that property.

public class PersonModel
{
  // helper properties
  public bool LocationNotSet { get { return string.IsNullOrEmpty(Location); } }       
  public bool NameAndAgeNotSet { get { return string.IsNullOrEmpty(Name) && Age <= 0; } }

  [RequiredIf("LocationNotSet")] // Requried if Location is not set
  public string Name {get; set;}
  [Range( 1, 5 ), RequiredIf("LocationNotSet")] // Requried if Location is not set
  public int Age {get; set;}

  [RequiredIf("NameAndAgeNotSet")] // Only required if Name and Age are not set.
  public string Location {get; set;}
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜