问题
I have a registration page that asks for your given Country and State. This is all fine and dandy when you are from a Country that has states like in the U.S, but when you are from a country that doesn't have states I would like to leave the state field blank and still allow you to register and submit the page still.
In my DB table for Country I have a column set boolean true if they have states and false if they don't. This way when someone selects a country with states it loads the states in the drop down menu.
I have the model set up like such.
[Required(ErrorMessage = "State is required.")]
public string StateId { get; set; }
I tried to make the value nullable but that seem to have no effect. So Is it even possible to do validation like this using DataAnnotations?
回答1:
As r3plica said, you could create a custom validation method to check the state. In the custom validation method, you could query the database and check whether the country has state and then return the validation result. Code as below:
public class DeveloperViewModel
{
public int Id { get; set; }
[Required(ErrorMessage = "Name is Required")]
public string Name { get; set; }
[Required(ErrorMessage ="Country is Required")]
public string Country { get; set; }
[RequiredIfHasState("Country", ErrorMessage ="State is Required")]
public string State { get; set; }
}
public class RequiredIfHasStateAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;
public RequiredIfHasStateAttribute(string comparisonProperty)
{
_comparisonProperty = comparisonProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
//get entered state value
var stateValue = (string)value;
var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
if (property == null)
throw new ArgumentException("Property with this name not found");
//get the country value
var countryValue = (string)property.GetValue(validationContext.ObjectInstance);
//get the current dbcontext
var _context = (MvcMovieContext)validationContext.GetService(typeof(MvcMovieContext));
//query the database and check whether the country has state.
if (_context.Countries.Where(c => c.CountryCode == countryValue).Select(c => c).FirstOrDefault().HasState)
{
if(stateValue == null)
{
//if country has state and the state is null. return error message
return new ValidationResult(ErrorMessage);
}
else
{
//if country has state and the state is not found.
if(!_context.Countries.Where(c => c.CountryCode == countryValue).Any(c => c.States.Any(e => e.StateName == stateValue)))
{
return new ValidationResult("State not found");
}
}
}
return ValidationResult.Success;
}
}
The screenshot as below (GB
country doesn't contain state, the US
country has states):
回答2:
This question is very similar to this:
Custom Validation Attributes: Comparing two properties in the same model
Basically, you create a custom data attribute. In your case (pseudo code)
public class MyViewModel
{
[RequiredIfHasState("End", ErrorMessage = "State is required.")]
public string StateId { get; set; }
public bool HasState { get; set; }
}
public class RequiredIfHasStateAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;
public RequiredIfHasStateAttribute(string comparisonProperty)
{
_comparisonProperty = comparisonProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
var currentValue = (bool)value;
var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
if (property == null)
throw new ArgumentException("Property with this name not found");
var comparisonValue = (string)property.GetValue(validationContext.ObjectInstance);
if (currentValue && string.IsNullOrEmpty(comparisonValue))
return new ValidationResult(ErrorMessage);
return ValidationResult.Success;
}
}
来源:https://stackoverflow.com/questions/63909558/model-required-value-for-only-given-scenario-when-form-is-submitted