ASP.NET MVC Conditional validation
Solution 1
I have solved this by handling the "ModelState" dictionary, which is contained by the controller. The ModelState dictionary includes all the members that have to be validated.
Here is the solution:
If you need to implement a conditional validation based on some field (e.g. if A=true, then B is required), while maintaining property level error messaging (this is not true for the custom validators that are on object level) you can achieve this by handling "ModelState", by simply removing unwanted validations from it.
...In some class...
public bool PropertyThatRequiredAnotherFieldToBeFilled
{
get;
set;
}
[Required(ErrorMessage = "*")]
public string DepentedProperty
{
get;
set;
}
...class continues...
...In some controller action ...
if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
this.ModelState.Remove("DepentedProperty");
}
...
With this we achieve conditional validation, while leaving everything else the same.
UPDATE:
This is my final implementation: I have used an interface on the model and the action attribute that validates the model which implements the said interface. Interface prescribes the Validate(ModelStateDictionary modelState) method. The attribute on action just calls the Validate(modelState) on IValidatorSomething.
I did not want to complicate this answer, so I did not mention the final implementation details (which, at the end, matter in production code).
Solution 2
There's a much better way to add conditional validation rules in MVC3; have your model inherit IValidatableObject
and implement the Validate
method:
public class Person : IValidatableObject
{
public string Name { get; set; }
public bool IsSenior { get; set; }
public Senior Senior { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (IsSenior && string.IsNullOrEmpty(Senior.Description))
yield return new ValidationResult("Description must be supplied.");
}
}
Read more at Introducing ASP.NET MVC 3 (Preview 1).
Solution 3
I had the same problem yesterday but I did it in a very clean way which works for both client side and server side validation.
Condition: Based on the value of other property in the model, you want to make another property required. Here is the code
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
Here PropertyName is the property on which you want to make your condition DesiredValue is the particular value of the PropertyName (property) for which your other property has to be validated for required
Say you have the following
public class User
{
public UserType UserType { get; set; }
[RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
public string Password
{
get;
set;
}
}
At last but not the least , register adapter for your attribute so that it can do client side validation (I put it in global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
Solution 4
I've been using this amazing nuget that does dynamic annotations ExpressiveAnnotations
You could validate any logic you can dream of:
public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
Solution 5
You can disable validators conditionally by removing errors from ModelState:
ModelState["DependentProperty"].Errors.Clear();
Peter Stegnar
Software developer, technology enthusiast, specialist in .NET, JavaScript, Angular and Cloud. Also really interested in quantum finance.
Updated on January 25, 2020Comments
-
Peter Stegnar over 4 years
How to use data annotations to do a conditional validation on model?
For example, lets say we have the following model (Person and Senior):
public class Person { [Required(ErrorMessage = "*")] public string Name { get; set; } public bool IsSenior { get; set; } public Senior Senior { get; set; } } public class Senior { [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value public string Description { get; set; } }
And the following view:
<%= Html.EditorFor(m => m.Name)%> <%= Html.ValidationMessageFor(m => m.Name)%> <%= Html.CheckBoxFor(m => m.IsSenior)%> <%= Html.ValidationMessageFor(m => m.IsSenior)%> <%= Html.CheckBoxFor(m => m.Senior.Description)%> <%= Html.ValidationMessageFor(m => m.Senior.Description)%>
I would like to be the "Senior.Description" property conditional required field based on the selection of the "IsSenior" propery (true -> required). How to implement conditional validation in ASP.NET MVC 2 with data annotations?
-
Darin Dimitrov over 14 yearsI've recently asked similar question: stackoverflow.com/questions/2280539/…
-
Steven over 14 yearsI'm confused. A
Senior
object is always a senior, so why can IsSenior be false in that case. Don't you just need the 'Person.Senior' property to be null whenPerson.IsSenior
is false. Or why not implement theIsSenior
property as follows:bool IsSenior { get { return this.Senior != null; } }
. -
Peter Stegnar over 14 yearsSteven: "IsSenior" translates to the checkbox field in the view. When user checks the "IsSenior" checkBox then the "Senior.Description" Field become mandatory.
-
Peter Stegnar over 14 yearsDarin Dimitrov: Well sort of, but not quite. You see, how would you achieve that the the error mesage is appent to the specific field? If you validate at object level, you get an error at object level. I need error on property level.
-
-
Peter Stegnar over 14 years"You need to validate at Person level, not on Senior level" Yes this is an option, but you loose the ability that the error is appended to particular field, that is required in the Senior object.
-
Kristof Claes over 14 yearsThe downside is that one of part your validation logic is located in the model and the other part in the controller(s).
-
Peter Stegnar over 14 yearsWell of course this is not necessary. I just show the most basic example. I have implemented this with interface on model and with action attribute that validates model which implements the mentioned interface. Interface perspires the Validate(ModelStateDictionary modelState) method. So finally you DO all validation in the model. Anyway, good point.
-
Aaron over 13 yearsI like the simplicity of this approach in the mean time until the MVC team builds something better out of the box. But does your solution work with client side validation enabled??
-
Peter Stegnar over 13 years@Aaron: I am happy that you like solution, but unfortunately this solution does not work with client side validation (as every validation attribute need its JavaScript implementation). You could help yourself with "Remote" attribute, so just Ajax call will be emitted to validate it.
-
Richard B about 13 yearsAre you able to expand on this answer? This makes some sense, but I want to make sure I'm crystal on it. I'm faced with this exact situation, and I want to get it resolved.
-
Jeyhun Rahimov over 11 yearsif property is "int" type, that requires value, if fill that field, Validate does not work..
-
Dan Hunex about 11 yearsThis is was the original starting point miroprocessordev.blogspot.com/2012/08/…
-
Peter Stegnar almost 11 years@Levitikon: Sure it does not, as any other custom validation implementation only on server side. There is also possibility to implement this validation that is supported on client side.
-
NightOwl888 almost 11 yearsUnfortunately, Microsoft put this in the wrong layer - validation is business logic and this interface is in the System.Web DLL. In order to use this, you have to give your business layer a dependency on a presentation technology.
-
viperguynaz almost 11 yearsyou do if you implement it - see full example at falconwebtech.com/post/…
-
User_MVC over 10 yearsIs there any equvalent solution in asp.net mvc2? ValidationResult, ValidationContext classes are not available in asp.net mvc2 (.net framework 3.5)
-
User_MVC over 10 yearsIs there any equvalent solution in asp.net mvc2? IValidatableObject inter face & ValidationResult, ValidationContext classes are not available in asp.net mvc2 (.net framework 3.5)
-
Dan Hunex over 10 years@User_MVC I didnt work on mvc2 and dont know but if there is any validation , try to extend that
-
Pakman over 10 yearsThis only works server-side as the linked blog states
-
Geethanga over 10 yearsI managed to get this working in the client side with MVC5, but in client it fires up the validation no matter what the DesiredValue is.
-
Jack over 9 years@Dan Hunex : In MVC4, I have not managed to work properly on client side and it fires up the validation no matter what the DesiredValue is. Any help pls?
-
twip over 8 yearsThe part about extending
DataAnnotationsModelValidator
was exactly what I needed to see. Thank you. -
Ammar Khan over 8 years@PeterStegnar will you please provide an example through an interface. I am more interested in to know how this could done with an interface.
-
Peter Stegnar over 8 years@AmmarKhan What do you mean thru interface? Usually you do not use interface in a MVC model ... Or what do you mean? Interface like UI?
-
Yulian about 8 yearsHow to make client side validation work: stackoverflow.com/a/15975880/2419808
-
Sudhanshu Mishra almost 8 yearsThe ExpressiveAnnotation library is the most flexible and generic solution of all answers here. Thanks for sharing!
-
Caverman about 7 yearsI've been banging my head trying to find a solution for a solid day. ExpressiveAnnotations looks to be the fix for me!
-
John Adam about 7 yearsWhat is
ErrorMessageResourceType
? I am passing null I am getting error. -
Smit Patel about 7 yearsfalconwebtech.com/post/… - @viperguynaz this isn't working
-
Doug Knudsen almost 7 yearsExpressiveAnnotation library is awesome!
-
Nattrass almost 7 yearsIt has client side support too!
-
gosr almost 7 yearsNo support for .NET Core though, and doesn't look like it'll happen.
-
RayLoveless over 6 yearsThis isn't working. How do you pass in the validationContext when calling myViewmodel.Validate(..)? Does Validate need to be called manually?
-
viperguynaz over 6 years@RayLoveless you should be calling
ModelState.IsValid
- not calling Validate directly -
RayLoveless over 6 years@viperguynaz thanks it's calling validate now but the validateMessageFor(m => m.myField) isn't showing the error message. How does the error message get bound to the specific viewModel's property? Thanks for your help.
-
viperguynaz over 6 yearsYou need to add a Validatio message to your view -
Html.ValidationMessage("SomProperty", "*")
-
Bil Simser over 5 yearsIMHO this should be marked as the correct answer. While you could build a custom attribute and all that, you're leaking something into the system that isn't necessary (unless you want to validate multiple models conditionally). This is the cleanest way and will be called in the controller when you (normally) do a if(ModelState.IsValid) call before saving/processing/etc.
-
xhafan almost 4 years.NET Core clone of ExpressiveAnnotations which works for me: github.com/uon-nuget/UoN.ExpressiveAnnotations.NetCore
-
user3280560 over 3 yearsI know this an older thread, but for completion - @viperguynaz - to bind the error to a specific property (e.g. for client side model binding validation error displaying), use the overloaded method instead. "yield return new ValidationResult("ErrorMessage.", new[] {"PutNameOfPropertyHere"}); Without this, the validation error is generic, and would require a catch-all warning label within the view, to be displayed.