简体   繁体   中英

MVC 3 how do I validate entire ViewModel?

I am trying to validate a Model where the rules are not always the same and depend on the other attributes within the model. What is the best way to go about doing this? Example below:

Hypothetical Example 1

Using MVVM pattern with MVC 3. My (hypothetical) ViewModel looks like this:

public string OrderType { get; set; }
public string Requestor { get; set; }
public int NumberOfPeanuts { get; set; }
public int NumberOfJellyBeans { get; set; }
public int NumberOfAlmonds { get; set; }

My view basically looks like this:

 @Html.EditorFor(model => model.OrderType ) 
 @Html.EditorFor(model => model.Requestor ) 
 @Html.EditorFor(model => model.NumberOfPeanuts ) 
 @Html.EditorFor(model => model.NumberOfJellyBeans ) 
 @Html.EditorFor(model => model.NumberOfAlmonds )

How would I implement validation that would return "Html.ValidationMessageFor" results for the following rules:

If OrderType = "Peanuts" then NumberOfPeanuts must be greater than 0, and NumberOfJellyBeans and NumberOfAlmonds MUST be null or 0, else display "this is a peanut-only order"

If OrderType = "Sample" then NumberOfPeanuts + NumberOfJellyBeans + NumberOfAlmonds must be less than 30, else display validation message "total amount of sample not high enough"

etc... etc...

You can extend ValidationAttribute with your own custom validation attribute.

public class CustomAttribute : ValidationAttribute
{    
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // validation logic
    }
}

I will expand on Sam's answer to use multiple model properties to validate:

public class PeanutOrderAttribute : ValidationAttribute, IClientValidatable
{
    private readonly string _peanutsProperty;
    private readonly string _orderTypeProperty;

    public PeanutOrderAttribute(string peanutsPropertyName, string orderTypePropertyName)
    {
        _peanutsProperty = peanutsPropertyName;
        _orderTypeProperty = orderTypePropertyName;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // get target property and value
        var peanutInfo = validationContext.ObjectType.GetProperty(this._peanutsProperty);
        var peanutValue = peanutInfo.GetValue(validationContext.ObjectInstance, null);

        // get compare property and value
        var ordertypeInfo = validationContext.ObjectType.GetProperty(this._orderTypeProperty);
        var ordertypeValue = ordertypeInfo.GetValue(validationContext.ObjectInstance, null);

        // if validation does not pass
        if (ordertypeValue == "Peanuts" && peanutValue < 1){
             return new ValidationResult("Error Message");
        }

        // else return success
        return ValidationResult.Success;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessageString,
            ValidationType = "PeanutOrder"
        };

        rule.ValidationParameters["peanuts"] = this._peanutsProperty;
        rule.ValidationParameters["ordertype"] = this._orderTypeProperty;

        yield return rule;
    }
}

Then put the validation tag on the appropriate model property:

[PeanutOrder("Peanuts", "OrderType")]
public int Peanuts{ get; set; }

public string OrderType { get; set; }

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM