开发者

How to make my custom data annotations work( it is in a list and each item in the list overrides the last validation test)

开发者 https://www.devze.com 2023-02-09 11:40 出处:网络
I am having problems with my custom validation in asp.net mvc 3.0 What I want to it to do. Be set on a property (right now I only can figure out how to make it on the class)

I am having problems with my custom validation in asp.net mvc 3.0

What I want to it to do.

  1. Be set on a property (right now I only can figure out how to make it on the class)
  2. Be smart enough to realize there are multiple instances being used.

Scenario

  1. textbox ( id = "textbox1")
  2. dropdownlist (id ="ddl1")
  3. textbox (id = "textbox2") 4 dropdownlist (id = "ddl2")

the values in the dropdownlist are the same. ("Days" and "Minutes")

Now user types in textbox1 30 and chooses "days" in ddl1. He then types in textbox2 10000 and "days" in ddl2. First one is valid second one is invalid as there is only 365 days in a year.

Scenario 2

User types in texbox1 99 and chooses "minutes" in ddl1. This of course is invalid and should fail.

So there could be any combination of where they might choose a vali开发者_StackOverflow社区d day and invalid minute time.

Or both could be valid

So I have this so far

My viewmodel

  [ReminderFormat(ErrorMessage =  "test")]
    public class ReminderViewModel
    {
        public List<SelectListItem> DurationTypes { get; set; }
        public DurationTypes SelectedDurationType { get; set; }

        public string ReminderLength { get; set; }
    }

This will be in a list of view models and generates the number of ones I need

List<ReminderViewModel> viewModel = new List<ReminderViewModel>()
// add ReminderviewModel to this collection

View

// do a for loop through the viewModel (  List<ReminderViewModel> )
// generate a textbox html box and drop down list for each one

Data annotation

    // set attribute to class for now but I want it on a property but I can't
    // figure out how to get SelectedDurationType  and pass it in. if I have my attribute on ReminderLength 
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]

public class ReminderFormatAttribute : ValidationAttribute
{

    public override bool IsValid(object value)
    {

        var reminderViewModel = (ReminderViewModel)value;


        switch (reminderViewModel.SelectedDurationType)
        {
            case DurationTypes.Days:
                int length = Convert.ToInt32(reminderViewModel.ReminderLength);
                if (length > 30)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

Problem 1

So as you can see I have the annotation on the class and I rather have it on the ReminderLength property.

Problem 2

Right now I just have days at 30 so it's less to type(I will change it later). The problem I am finding right now is that if textbox1 has 31 in it this will fail my validation.

This is correct. But if I have textbox2 that has 1 as the value it will pass validation. This is also correct.

What is not correct is that it will override the first invalid validation. So now it thinks all validation passed and goes into my action method. When it should reject it and go back to the view and tell the user that textbox1 failed validation.


Here's what I would do for your validation attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class ReminderFormatAttribute: ValidationAttribute, IClientValidatable
{
    public string DurationProperty { get; set; }

    public ReminderFormatAttribute(string durationProperty)
    {
        DurationProperty = durationProperty;
        ErrorMessage = "{0} value doesn't work for selected duration";
    }

    public override string FormatErrorMessage(string propName)
    {
        return string.Format(ErrorMessage, propName);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var durationType = validationContext.ObjectType.GetProperty(DurationProperty).GetValue(validationContext.ObjectInstance, null);
        var reminderLength = value;

        // Do your switch statement on durationType here
        // Here is a sample of the correct return values
        switch (durationType)
        {
            case DurationTypes.Days:
                if (reminderLength > 30)
                {
                    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
                }
                else
                {
                    return null;
                }
        }
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
                      {
                        ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                        ValidationType = "reminderformat",
                      };
        rule.ValidationParameters["durationproperty"] = DurationProperty;
        yield return rule;
    } 
}

Now in your ViewModel you can annotate the ReminderLength property like so:

[ReminderFormat("SelectedDurationType")]
public string ReminderLength { get; set; }

This is how I usually do it when the validation depends on the value of another property. The GetClientValidationRules method is the server side piece you need to tie into the unobtrusive client validation via jquery validate. Check out this link for another ValidationAttribute example: http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net-mvc-3-part-2

The example on this site also goes into writing the necessary jQuery to tie in the client validation

0

精彩评论

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