Как добавить метаданные в динамически создаваемую MVC3 ViewModel?

Одной из ключевых особенностей проекта, над которым я работаю, является возможность для пользователя настраивать формы (как в "формах" для заполнения) на основе пула ранее существующих типов полей (например, хорошо известных типов). "имя пользователя", "дата рождения" и т. д., но также "универсальные типы", такие как "string", "DateTime" и т. д.).

Раньше у нас был статический ViewModel, который прекрасно работал для "хорошо известных" типов и выглядел так:

public class UserInputModel
{
    [StringLength(200)]
    public string Name { get; set; }

    [Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
    public DateTime BirthDate { get; set; }

    //Here comes a lot of other properties
}

Все известные свойства были перечислены, и мы показывали или скрывали их с учетом контекста.

Но последнее требование пришло и изменило все это. Теперь пользователь сможет добавлять столько полей универсального типа, сколько он хочет. Для этого мы решили сделать эту InputModel полностью динамичной. Теперь это выглядит так:

public class UserInputModel
{
    // Each ModelProperty has an "Id" and a "Value" property
    public ICollection<ModelProperty> Properties { get; set; }
}

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

@Html.TextBoxFor(m => m.Properties[index].Value);

... и мы получаем данные обратно в виде заполненной формы.

=> Это работает нормально, но у нас нет никакой проверки на стороне клиента. Для этого нам понадобятся метаданные... которых у нас больше нет с помощью аннотаций, поскольку мы динамически создаем модель.

Чтобы предоставить эти метаданные, я создал CustomModelMetadataProvider что наследует от DataAnnotationsModelMetadataProvider и зарегистрировал его как новый ModelMetadataProvider в Global.asax. CreateMetadata() Функция вызывается при создании ViewModel, и это для каждого из свойств моей ViewModel... Sofar так хорошо.

Где начинается проблема: чтобы добавить некоторые метаданные к текущему свойству, мне сначала нужно определить, какое свойство я сейчас просматриваю ("Имя" имеет максимальную длину 200, а "дата рождения" - нет, поэтому я не могу назначить максимальная длина каждого свойства по умолчанию). И почему-то мне так и не удалось сделать это, так как все свойства имеют одинаковое имя Value и тот же тип контейнера ModelProperty,

Я попытался получить доступ к контейнеру свойства через отражение, но поскольку целью ModelAccessor является сама ViewModel (из-за лямбда-выражения m => m.Properties), следующая конструкция дает мне ViewModel в целом, а не только ModelProperty:

var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);

Я переворачиваю это снова и снова, но не могу найти способ определить, какой ModelProperty у меня в руках. Есть ли способ сделать это?

Обновление: после того, как какое-то время мы перебирали это во всех возможных направлениях, мы наконец пошли другим путем. Мы в основном используем ненавязчивый javascript, чтобы использовать возможности валидации MVC, не касаясь атрибутов и метаданных. Короче говоря, мы добавляем атрибуты HTML, такие как value-data="true" (и все другие обязательные атрибуты) @Html.TextBoxFor() заявления. Это прекрасно работает для всех атомарных проверок (требуется, длина строки и т. Д.).

2 ответа

Тим, ты можешь использовать то, что кажется проверкой на стороне клиента через Ajax с атрибутом Remote в твоих свойствах.

По сути, вам нужно настроить контроллер проверки, а затем записать в него несколько смартов. Но, по крайней мере, вы сможете написать несколько вспомогательных методов и хранить все это в одном месте. У вас будет ряд валидаторов, основанных на метаданных, которые вы представляете конечным пользователям, и каждый метод валидатора будет работать для определенного типа с хорошим повторным использованием.

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

Надеюсь это поможет.

Посмотрите, поможет ли вам эта статья: Техника переноса метаданных для просмотра моделей с помощью AutoMapper.

Также используйте это для идей (поставщик метаданных пользовательской модели): изменение атрибута MetadataType viewmodel во время выполнения

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

Обновить

Попробуй использовать ModelMetadata и переопределить ModelMetadataProvider: Погрузитесь в MVC: ModelMetadata и ModelMetadataProvider. Таким образом, вы полностью настраиваете метаданные модели (это заменяет аннотации данных) и вы полностью контролируете происходящее, а не полагаетесь на ASP.NET MVC.

Еще одно хорошее место, чтобы взглянуть на это - создание собственного ModelMetadataProvider для обработки пользовательских атрибутов.

Надеюсь, что все это поможет вам.

Другие вопросы по тегам