ASP.NET MVC ValidationAttribute Получает другое отображаемое имя свойства

#asp.net-mvc #validation #client-side-validation #validationattribute

#asp.net-mvc #проверка #проверка на стороне клиента #validationattribute

Вопрос:

Я создал пользовательский атрибут проверки CompareLessThan, скопировав ASP.NET MVC 3 CompareAttribute и вместо проверки на равенство я проверяю, чтобы увидеть, что одно свойство меньше другого. При возникновении ошибки на стороне клиента пользователю отображается сообщение «{0} должно быть меньше {1}».

Моя модель настроена следующим образом с отображаемыми атрибутами, ссылающимися на файл ресурсов.

 [CompareLessThan("AmountAvailable", ErrorMessageResourceName="CompareLessThan", ErrorMessageResourceType = typeof(Resources.ValidationMessages))]
[Display(Name = "Amount", ResourceType = typeof(Resources.Labels))]
public decimal Amount { get; set; }

[Display(Name = "AmountAvailable", ResourceType = typeof(Resources.Labels))]
public decimal AmountAvailable { get; set; }
  

Тогда пользовательский метод проверки GetClientValidationRules точно такой же, как и в CompareAttribute

 public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{            
    yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(OtherProperty), this.AllowEquality);
}
  

Здесь мы генерируем сообщение об ошибке, которое будет отображаться пользователю, если возникнет проблема. Я могу получить отображаемое имя из файла ресурсов для свойства, которое украшено моим пользовательским атрибутом CompareLessThan, но мой вопрос в том, как мне получить отображаемое имя «другого» свойства, с которым мы сравниваем? В методе isValid у нас есть ссылка на ValidationContext, из которого я могу сгенерировать объект PropertyInfo для свойства «other» и, я думаю, получить отображаемое имя. Но в GetClientValidationRules у меня нет к этому доступа.

Я всегда мог просто передать другое значение для отображаемого имени другого свойства, но я надеялся, что будет способ получить его, поскольку я уже указываю его с помощью аннотаций данных.

Есть идеи?

Ответ №1:

По состоянию на ASP.NET MVC 4 вот как мне удалось получить другое свойство:

 PropertyInfo otherPropertyInfo =
                  this.Metadata.ContainerType.GetProperty(attribute.DependentProperty);
  

Затем я получил Display attribute из свойства:

 var displayAttribute =
    otherPropertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).
    FirstOrDefault() as DisplayProperty;
  

В вашем случае:

 // GetName() is important to get the translated name if you're using a resource file...
this.otherPropertyDisplayName = displayAttribute.GetName();
  

GetName() ссылка:

http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.name(v=vs.95).aspx

Комментарии:

1. Это должно было быть «как DisplayProperty» или «как DisplayAttribute»? Мой не работал для DisplayProperty, но отлично работал для DisplayAttribute. Отличное решение в противном случае.

2. @MVCKarl: Я думаю, вы правы… Возможно, это была небольшая путаница при вводе ответа здесь. Я не могу записать, что именно я сделал в то время. 🙂

Ответ №2:

Ответ, предоставленный nemesv, не работал в качестве метаданных.Свойство модели имеет значение 0. Но благодаря метаданным у нас есть полное имя модели, поэтому можно создать новый экземпляр этой модели, а затем создать новый DataAnnonationsModelMetadataProvider из этого экземпляра create. Оттуда мы можем получить отображаемое имя другого свойства.

 public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
    Type type = Type.GetType(metadata.ContainerType.FullName);
    var model = Activator.CreateInstance(type);

    var provider = new DataAnnotationsModelMetadataProvider();
    var otherMetaData = provider.GetMetadataForProperty(() => model, type, this.OtherProperty);

    this.otherPropertyDisplayName = otherMetaData.DisplayName;

    yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(this.OtherProperty), this.AllowEquality);
}
  

Мне действительно не нравится это решение (хотя оно работает), поскольку кажется, что должен быть лучший способ. У кого-нибудь еще есть другие идеи?

Ответ №3:

Я не пробовал, но вы можете получить свойства модели с metadata.Properties помощью свойства

 metadata.Properties.Single(p => p.PropertyName == "OtherPropName").DisplayName;
  

РЕДАКТИРОВАТЬ: поскольку свойства пусты, что вы всегда можете сделать (хотя это очень элегантно). Вы можете сгенерировать метаданные для себя.

 var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => metaData.Model, metaData.ModelType, "OtherPropertyName");
  

Комментарии:

1. Хорошая мысль, но коллекция свойств всегда пуста.

2. @NickOlsen Это печально. Я обновил свой ответ некоторым «обходным путем».

3. Это также не работает как метаданные. Свойство модели — это просто значение 0. Используя вашу логику, я смог найти другой способ сделать это (см. Другой ответ), но мне это не очень нравится. Я надеюсь, что есть лучший способ, который может предоставить кто-то другой.