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

Я создал собственный атрибут проверки CompareLessThan, скопировав атрибут CompareAttribute ASP.NET MVC 3, и вместо проверки на равенство я проверяю, что одно свойство меньше другого. При возникновении ошибки на стороне клиента пользователю отображается сообщение "{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 для свойства «другое» и получить отображаемое имя. Но в GetClientValidationRules у меня нет к этому доступа.

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

Есть идеи?


person Nick Olsen    schedule 13.10.2011    source источник


Ответы (3)


Начиная с 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%28v=vs.95%29.aspx

person Leniel Maccaferri    schedule 25.06.2012
comment
Это должно было быть как DisplayProperty или как DisplayAttribute? Мой не работал для DisplayProperty, но отлично работал для DisplayAttribute. В остальном отличное решение. - person MVCKarl; 08.05.2013
comment
@MVCKarl: Я думаю, вы правы ... Может быть, я немного запутался при вводе ответа здесь. Я не могу записать, что именно я делал в это время. :) - person Leniel Maccaferri; 08.05.2013

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

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);
}

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

person Nick Olsen    schedule 14.10.2011

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

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

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

var provider = new DataAnnotationsModelMetadataProvider();
var otherMetaData = provider.GetMetadataForProperty(() => metaData.Model, metaData.ModelType, "OtherPropertyName");
person nemesv    schedule 14.10.2011
comment
Хорошая мысль, но коллекция свойств всегда пуста. - person Nick Olsen; 14.10.2011
comment
@NickOlsen Это грустно. Я обновил свой ответ некоторым обходным путем. - person nemesv; 14.10.2011
comment
Это также не работает, поскольку свойство metadata.Model имеет просто значение 0. Используя вашу логику, я смог найти другой способ сделать это (см. Другой ответ), но мне это не очень нравится. Я надеюсь, что есть лучший способ, который может предоставить кто-то другой. - person Nick Olsen; 14.10.2011