Одна возможность - написать пользовательский атрибут проверки:
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
, которое вы примените к одному из свойств модели представления:
public class MyViewModel
{
[RequiredIfOtherFieldIsNull("Mobile")]
public string Phone { get; set; }
public string Mobile { get; set; }
}
тогда у вас может быть контроллер:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
и, наконец, представление, в котором вы зарегистрируете адаптер для подключения проверки на стороне клиента для этого пользовательского правила:
@model MyViewModel
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add(
'requiredif', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
}
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['requiredif'] = element;
if (options.message) {
options.messages['requiredif'] = options.message;
}
}
);
jQuery.validator.addMethod('requiredif', function (value, element, params) {
var otherValue = $(params).val();
if (otherValue != null && otherValue != '') {
return true;
}
return value != null && value != '';
}, '');
</script>
@using (Html.BeginForm())
{
<div>
@Html.LabelFor(x => x.Phone)
@Html.EditorFor(x => x.Phone)
@Html.ValidationMessageFor(x => x.Phone)
</div>
<div>
@Html.LabelFor(x => x.Mobile)
@Html.EditorFor(x => x.Mobile)
@Html.ValidationMessageFor(x => x.Mobile)
</div>
<button type="submit">OK</button>
}
Довольно больные вещи для чего-то настолько простого, как правило валидации, с которым мы сталкиваемся в нашей повседневной жизни. Я не знаю, о чем думали разработчики ASP.NET MVC, когда решили выбрать декларативный подход для проверки вместо императивного.
В любом случае, поэтому я использую FluentValidation.NET вместо аннотаций данных для проверки моих моделей. Реализация таких простых сценариев проверки реализована так, как должно быть - просто.