Peter Stegnar Peter Stegnar - 3 months ago 61
C# Question

ASP.NET MVC Conditional validation

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?

Answer

I have solved it with handling the "ModelState" dictionary which is contained by the controller. ModelState dictionary include all the members that are 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 requited), while maintain property level error message (this is not true for the custom validators that are on object level) you can achieve this by handling "ModelState" by simply remove 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");
}

...

By this we achieve conditional validation, while leaving everything else the same.


UPDATE:

My final implementation look like that I have implemented it with an interface on model and action attribute that validates model which implements the mentioned interface. Interface prescribes the Validate(ModelStateDictionary modelState) method. Attribute on action just call the Validate(modelState) on IValidatiorSomething.

I did not want to complicate this answer, that way I did not mention the final implementation details, with at the end in production code matters.