Вот лучшее решение, которое я нашел для более сложной проверки, помимо простой модели аннотаций данных.
Я уверен, что я не одинок в попытках реализовать IDataErrorInfo
и увидеть, что он создал для меня только два метода для реализации. Я думаю, подожди минутку - я должен пойти туда и написать свои собственные стандартные процедуры для всего теперь с нуля? А также - что, если у меня есть вещи на уровне модели для проверки. Кажется, что вы сами по себе, когда решаете использовать его, если только вы не хотите сделать что-то , например или this изнутри вашей реализации IDataErrorInfo.
У меня возникла та же проблема, что и у спрашивающего. Я хотел проверить американский почтовый индекс, но только если страна была выбрана в качестве США. Очевидно, что аннотация данных на уровне модели не будет хорошей, потому что это не приведет к тому, что почтовый индекс будет выделен красным цветом как ошибка. [хороший пример аннотации данных уровня класса можно найти в примере проекта MVC 2 в классе PropertiesMustMatchAttribute].
Решение довольно простое:
Сначала вам нужно зарегистрировать связыватель моделей в global.asax. Вы можете сделать это как уровень класса [атрибут], если хотите, но я считаю, что регистрация в global.asax более гибкая.
private void RegisterModelBinders()
{
ModelBinders.Binders[typeof(UI.Address)] = new AddressModelBinder();
}
Затем создайте класс связующего компонента и напишите свою сложную проверку. У вас есть полный доступ ко всем свойствам объекта. Это запустит после любых аннотаций данных, поэтому вы всегда можете очистить состояние модели, если хотите изменить поведение по умолчанию любых атрибутов проверки.
public class AddressModelBinder : DefaultModelBinder
{
protected override void OnModelUpdated(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
base.OnModelUpdated(controllerContext, bindingContext);
// get the address to validate
var address = (Address)bindingContext.Model;
// validate US zipcode
if (address.CountryCode == "US")
{
if (new Regex(@"^\d{5}([\-]\d{4})?$", RegexOptions.Compiled).
Match(address.ZipOrPostal ?? "").Success == false)
{
// not a valid zipcode so highlight the zipcode field
var ms = bindingContext.ModelState;
ms.AddModelError(bindingContext.ModelName + ".ZipOrPostal",
"The value " + address.ZipOrPostal + " is not a valid zipcode");
}
}
else {
// we don't care about the rest of the world right now
// so just rely on a [Required] attribute on ZipOrPostal
}
// all other modelbinding attributes such as [Required]
// will be processed as normal
}
}
Прелесть этого в том, что все ваши существующие атрибуты проверки будут по-прежнему работать - [Обязательно], [EmailValidator], [MyCustomValidator] - независимо от того, что у вас есть.
Вы можете просто добавить любой дополнительный код в связыватель модели и задать поле или ошибки ModelState на уровне модели, как вам нужно.
Обратите внимание, что для меня Address
является дочерним элементом основной модели - в данном случае CheckoutModel
, который выглядит следующим образом:
public class CheckoutModel
{
// uses AddressModelBinder
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }
// etc.
}
Вот почему я должен сделать bindingContext.ModelName
+ ".ZipOrPostal"
, чтобы ошибка модели была установлена для 'BillingAddress.ZipOrPostal' и 'ShippingAddress.ZipOrPostal'.
PS. Любые комментарии от «типов модульного тестирования» приветствуются. Я не уверен насчет влияния этого на юнит-тестирование.