Проблема проверки модели MVC 3 - надзор или дизайн
Я столкнулся с сценарием, где мне нужно было знать, какое свойство в настоящее время проверялось в пользовательском ValidationAttribute
. Я предположил, что это будет легко в MVC 3, поскольку ValidationContext
передается в метод IsValid
.
Не вдаваясь в подробности, вот основная идея:
protected override ValidationResult IsValid(Object value, ValidationContext validationContext) {
if (ShouldICareAboutYou(validationContext.MemberName))
{
//Do some stuff
}
//Return the results
}
Это казалось идеальным решением, и действительно, когда модульное тестирование моего пользовательского ValidationAttribute с помощью Validator.TryValidateObject
, все работало красиво!
ОДНАКО...
При вызове TryUpdateModel
или TryValidateModel
внутри моего контроллера выполняется проверка, но ValidationContext.MemberName
имеет значение null.
Whaa Huh?!?
Я сделал небольшое исследование и, конечно, прямо внутри DataAnnotationsModelValidator
- это код... или его отсутствие.
public override IEnumerable<ModelValidationResult> Validate(object container) {
// Per the WCF RIA Services team, instance can never be null (if you have
// no parent, you pass yourself for the "instance" parameter).
ValidationContext context = new ValidationContext(container ?? Metadata.Model, null, null);
context.DisplayName = Metadata.GetDisplayName();
// Setting the MemberName here would be trivial!
// However, philh told me not to. Something about
// a guy named Josh who pushed him down on the playground
// in middle school.
//context.MemberName = Metadata.PropertyName; (Suck It, Josh!!!)
ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context);
if (result != ValidationResult.Success) {
yield return new ModelValidationResult {
Message = result.ErrorMessage
};
}
}
Я понимаю, что DisplayName
может быть именем свойства, если в свойство не было применено DisplayAttribute
. К сожалению, я не могу заниматься гипотезами. Мне нужно знать точно, что такое имя свойства.
Так в чем заключена сделка? Является ли это по дизайну или честному надзору. Если это недосмотр, тогда было бы здорово получить это исправление в MVC 4:)
Отказ от ответственности:
Добавленные комментарии в приведенном выше примере кода должны быть смешными. Я не знаю, и никогда не встречал Phil Haack. Из того, что я могу сказать, он кажется очень хорошим парнем. И толкание его в средней школе сделало бы меня королевским душем!
Ответы
Ответ 1
У меня была такая же проблема, и я решил передать имя свойства в качестве параметра в конструкторе атрибутов, а затем сохранить его в атрибуте. Например:
[MyValidationAttribute("MyProperty")]
public string MyProperty { get; set; }
Затем в MyValidationAttribute.cs:
public class MyValidationAttribute
{
private string PropertyName;
public MyValidationAttribute(string propertyName)
{
this.PropertyName = propertyName;
}
}
Немного раздражает, что теперь мне нужно дважды ввести имя моего имущества, но оно решает проблему.
Ответ 2
Джош
Разочарование, да.
Однако для ваших целей вы можете создать свой собственный класс, наследующий от DataAnnotationsModelValidator
, переопределить метод Validate()
и раскомментировать эту строку кода, которая насмехается над вами. Затем в Global.asax.cs очистите ModelValidatorProviders.Providers
и добавьте свой класс.
Не оптимальное решение, но оно поможет вам добраться туда.
Ответ 3
Вам нужно вызвать метод DataAnnotationsModelValidatorProvider.RegisterAdapter
или DataAnnotationsModelValidatorProvider.RegisterAdapterFactory
для вашего типа атрибута и предоставить свой пользовательский ModelValidator
.
Ответ 4
Если бы та же проблема, и этот вопрос поставил меня на правильные пути. Я исправил его, изменив свой собственный валидатор, чтобы заполнить имя_пользователя, когда создается ValidationResult, так же (обратите внимание на второй параметр в конструкторе ValidationResult):
protected override ValidationResult IsValid(Object value, ValidationContext validationContext) {
if (ShouldICareAboutYou(validationContext.MemberName))
{
//Do some stuff
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new string[] { validationContext.MemberName });
}