I have a model (simplified) as follows:
public UserModel {
...
public USState State {get; set; }
public string StateString {get; set; }
public Country Country {get; set; }
...
}
The validation rules I need are:
- If
Country
is USA then State
is required.
- If
Country
is not USA then StateString
is required.
I've created a custom validation attribute RequiredIfAttribute
. This works fine so I'm not going to fill the question with it's implementation. It has three required members:
CompareField
- This is the field it will use to check whether or not validation is required.
CompareValue
- This is the value it will compare to to decide whether or not validation is required.
CompareType
- This is how it will compare the value to decide whether or not validation is required.
So with this, I update my model as such:
public UserModel {
...
[RequiredIf("Country", Country.USA, EqualityType.Equals)]
public USState State {get; set; }
[RequiredIf("Country", Country.USA, EqualityType.NotEquals)]
public string StateString {get; set; }
[Required]
public Country Country {get; set; }
...
}
I should note here that my RequiredIfAttribute
also has client side validation. This works perfectly.
Now on to the problem...
I'm posting the following values:
State = AL
StateString = null
Country = USA
This meets my validation rules and should be valid. Here's the but. ModelState
is telling me it is not valid. Apparently by StateString field is required. That's not what I specified. Why aren't my validation rules applied as expected?
(If you know what's wrong at this point, then don't feel obliged to read the rest of the question)
So here's what's happening. The RequiredIfAttribute
is being triggered three times. But wait, I'm only using it twice. It is is being triggered like so:
- Trigger on
StateString
(this comes back invalid)
- Trigger on
State
(this comes back valid)
- Trigger on
StateString
(this comes back valid)
This is pretty odd. It's validating StateString
twice, first time it passes, second time it fails. The plot thickens...
I looked into this further to find that the first time it tries to validate StateString
, Country
is not set. The second time it tries to validate StateString
, Country
is set. Looking closer, it seem that the first attempt to validate StateString
has occurred before my model has been fully bound. All properties (not listed in the sample model) that are below StateString
(in code) are not bound. The second attempt to validate StateString
, all properties are bound.
I have solved the problem, but I'm not confident in it because I simply do not trust it. To get my validation to work as expected, I rearranged the model as such (attributes removed for brevity):
public UserModel {
...
public Country Country {get; set; }
public USState State {get; set; }
public string StateString {get; set; }
...
}
The RequiredIfAttribute
still triggers three times as above but ModelState
tells me that the posted data (as above) is now valid, like magic!
What I'm seeing is this (my assumptions):
1. Start binding (property by property, top to bottom in code (risky))
2. Arrive at `StateString` and decide to try and validate
3. Finish binding
4. Validate all properties
I really have two questions:
1. Why is this behaviour exhibited?
2. How can I stop this behaviour?