Title: How to make my custom data annotations work( it is in a list and each item in the list overrides the last validation test)
Tags: c#;asp.net-mvc;validation;asp.net-mvc-3;data-annotations
Question: 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)
Be smart enough to realize there are multiple instances being used.
Scenario
textbox ( id = "textbox1")
dropdownlist (id ="ddl1")
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 valid 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&gt; 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&gt; viewModel = new List<ReminderViewModel&gt;()
// add ReminderviewModel to this collection
// do a for loop through the viewModel (  List<ReminderViewModel&gt; )
// 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 &gt; 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.
Comment: I worked on a project in MVC 2, where we had really complex validation rules, lots of rules (some silly ;-) ) and many dependancies..... In the end I found it much easier to accomplish such validation using the fluent validation framework: check htt://fluentvalidation.codeplex.com

Comment: @chobo2. I may well be out of date on this one, but the last time I looked at this, there was no framework for client side validation with complex rules, expcially with dependacies on other objects in your application core. Simple client side validation is possible in FV, such as max length, range or a regex. here is an example with MVC http://fluentvalidation.codeplex.com/wikipage?title=mvc&referringTitle=Documentation

Comment: Yes, A solution we came up with where it was essentially to write custom JQuery client side validation scripts and include this scripts on the pages that needed them. This  was a specific jquery script seperate from our custom fluent validation (Breaking the DRY :-( principle with two sets of validation). The alternative was to write a parser that parses your custom  fluentvalidation rules (or even your custom data annotations) and generates the appropriate non-obtrusive JQuery Javascript required to validate your page. It is possible, but we did not have the time to implement this.

Comment: @ Dai Bok - Interesting I see that it works for client side but I don't see any examples. Does it use jquery validate and unobstructive javascript?

Comment: @ Dai Bok - Unless it is a supported complex validation that is built in then there should be. However if it is custom then of course no since you got to build the server side and client side. So I am wondering if I make my own validation can I write the jquery validate part to do the client side if I use fluent?

Comment: @Dai Bok  - Well I guess someone will break the DRY principle either us or the people who made the libraries as all of theirs support both server side and clientside.

Comment: chobo2, did @bbak's provided answer solve your problem?  I'm running into the same situation and am curious how you resolved this


Here is another answer: 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 &gt; 30)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
else
{
return null;
}
}
public IEnumerable<ModelClientValidationRule&gt; 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

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 7, 'ContentLicense': 'CC BY-SA 2.5', 'CreationDate': '2011:00:02/06/11', 'FavoriteCount': 2.0, 'PostTypeId': 1, 'Tags': '<c#><asp.net-mvc><validation><asp.net-mvc-3><data-annotations>', 'Title': 'How to make my custom data annotations work( it is in a list and each item in the list overrides the last validation test)', 'ViewCount': 4150, 'id': 4916467, 'user_AboutMe': '', 'user_AccountId': 44316.0, 'user_DisplayName': 'chobo2', 'user_Reputation': 80736.0, 'user_Views': 4243.0, 'user_id': 130015.0}}