Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I read on some post, but cant find it now that in MVC 3 it was not really needed to create a Validator, only the Attribute. Is this true? I do say I find it confusing that the attribute has the IClientValidatable on it. So what does the DataAnnotationsModelValidator class do if the annotation has the client side script name (IClientValidatable), and the ability to validate (ValidationAttribute IsValid)?

It would be really nice if I didnt have to register the Attribute with the Validator in the global. Can this be done? Did I read some bad advise?

EDIT: Interestingly enough I just tested it by excluding the validator, put all the logic in IsValid and it works great. I guess the only thing that might be missing would be the controller context, but I am not sure that is useful in validation. The IsValid has ValidationContext which has ServiceContainer if I needed a service. Any real disadvantage I am not picking up on here?

EDIT 2: I will start with a validator from this example: http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

The Attribute:

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    private RequiredAttribute innerAttribute = new RequiredAttribute();
    public string DependentProperty { get; set; }
    public object TargetValue { get; set; }

    public RequiredIfAttribute(string dependentProperty, object targetValue)
    {
        this.DependentProperty = dependentProperty;
        this.TargetValue = targetValue;
    }

    public override bool IsValid(object value)
    {
        return innerAttribute.IsValid(value);
    }

    public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        ModelClientValidationRule modelClientValidationRule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.DisplayName),
            ValidationType = "requiredifattribute"
        };
        modelClientValidationRule.ValidationParameters.Add("requiredifattribute", DependentProperty);
        yield return modelClientValidationRule;
    }
}

The Validator:

public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
    public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
        : base(metadata, context, attribute)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        return base.GetClientValidationRules();
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
        var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
        if (field != null)
        {
            var value = field.GetValue(container, null);
            if ((value == null && Attribute.TargetValue == null) ||
                (value.Equals(Attribute.TargetValue)))
            {
                if (!Attribute.IsValid(Metadata.Model))
                    yield return new ModelValidationResult { Message = ErrorMessage };
            }
        }
    }
}

With the current code above, I need to register in the Global.asax.cs file:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredIfValidator));

But if I move everything into just the attribute, I dont have to register it:

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    private RequiredAttribute innerAttribute = new RequiredAttribute();
    public string DependentProperty { get; set; }
    public object TargetValue { get; set; }

    public RequiredIfAttribute(string dependentProperty, object targetValue)
    {
        this.DependentProperty = dependentProperty;
        this.TargetValue = targetValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var field = validationContext.ObjectInstance.GetType().GetProperty(DependentProperty);
        if (field != null)
        {
            var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
            if ((dependentValue == null && TargetValue == null) ||
                (dependentValue.Equals(TargetValue)))
            {
                if (!innerAttribute.IsValid(value))
                    return new ValidationResult(ErrorMessage);
            }
        }
        return ValidationResult.Success;
    }

    public System.Collections.Generic.IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        ModelClientValidationRule modelClientValidationRule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.DisplayName),
            ValidationType = "requiredifattribute"
        };
        modelClientValidationRule.ValidationParameters.Add("requiredifattribute", DependentProperty);
        yield return modelClientValidationRule;
    }
}

Is there an problem with the last bit of code replacing all of the other code? Why would I keep the validator class?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
425 views
Welcome To Ask or Share your Answers For Others

1 Answer

CrazyDart,

The IClientValidatable interface was added in MVC3.

Your second example shows a valid use of this new interface. You are correct that it does not have to be registered, and it will provide the necessary client side rules for validation, as well as doing the necessary server side validation.

Go ahead, enjoy it.

counsellorben


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...