Мы разрабатываем приложение, в котором схема конечного пользователя является динамической (для этого у нас есть хороший бизнес-сценарий - это не то, с чем легко справляется статическая модель).
Я использовал класс .NET DynamicObject, чтобы позволить этим объектам динамических схем легко обращаться с кодом, и ожидал, что это будет просто работать с метаданными модели MVC. Однако поддержка метаданных MVC, похоже, затруднена тем, что она касается только метаданных, определенных для каждого типа, а не для каждого объекта, который будет иметь место здесь.
Даже когда я откопал и попытался реализовать собственный ModelMetadataProvider, кажется, что необходимая информация просто не проходит - метод GetMetadataForProperty особенно проблематичен. Фактически мне нужно получить доступ к родительскому или контейнерному объекту для свойства, но все, что передается, это тип.
Вышеупомянутое вызывается в основном из метода FromStringExpression в классе ModelMetadata. Этот метод фактически имеет контейнер (по крайней мере, в этом случае), но не проходит его. Эта ветка выполняется, когда она находит данные представления о сохраненном выражении (кэшированном?) В ViewData. Если это не удается, оно возвращается к просмотру через объект ModelMetadata, что иронически может сработать для меня. Особенно раздражает то, что метод FromStringExpression является статическим, поэтому я не могу легко переопределить его поведение.
В отчаянии я рассмотрел попытку пересечения выражения modelAccessor, но это похоже на kludge в лучшем случае и очень хрупкое.
Я искал экстенсивно для решения этого. Многие указывают на разговор Брэда Уилсона (http://channel9.msdn.com/Series/mvcConf/mvcConf-2011-Brad-Wilson-Advanced-MVC-3) по неклассическим моделям, однако, если вы посмотрите на фактический представленный код, вы увидите, что он TOO привязан к ТИП, а не объект - другими словами, не очень полезно. Другие указали на http://fluentvalidation.codeplex.com/, но похоже, что это относится только к стороне проверки, и я подозреваю, что страдает от той же проблемы (привязанной к типу, а не к объекту) как указано выше.
Например, у меня может быть словарь, содержащий ряд объектов поля. Это выглядит примерно так (очень сокращенный/упрощенный пример):
public class Entity : DynamicObject, ICustomTypeDescriptor
{
public Guid ID { get; set; }
public Dictionary<string, EntityProp> Props { get; set; }
... DynamicObject and ICustomTypeDescriptor implementation to expose Props as dynamic properties against this Entity ...
}
public class EntityProp
{
public string Name { get; set; }
public object Value { get; set; }
public Type Type { get; set; }
public bool IsRequired { get; set; }
}
Это может быть передано виду в виде его режима просмотра (или его части), и, на мой взгляд, я хотел бы использовать:
@Html.EditorForModel()
Кто-нибудь нашел способ обойти это?
Я определил два возможных альтернативных подхода, но оба имеют существенные недостатки:
- Отказаться от использования MVC ModelMetadata для этого и вместо этого создать модель представления, которая непосредственно содержит необходимые метаданные, вместе с шаблонами, необходимыми для отображения этих более сложных объектов модели представления. Значит, однако, что мне тогда придется относиться к этим объектам по-разному к "нормальным" объектам, несколько навредив цели и увеличив количество шаблонов представлений, которые нам нужно построить. Это подход, к которому я склоняюсь сейчас - более или менее отказ от интеграции с материалом MVC ModelMetadatastrong >
- Создайте уникальный ключ для каждого шаблонного свойства и используйте его для имени свойства, а не для отображаемого имени, которое позволит ModelMetadataProvider найти метаданные, связанные с этим свойством, без ссылки на его родительский элемент. Однако это привело бы к довольно уродливой ситуации при отладке и снова показалось бы крупномасштабным kludge. Я сейчас попробовал упрощенную версию этого и, похоже, работает, но имеет какое-то нежелательное поведение, например, нужно использовать бессмысленное имя свойства, если я хочу явно привязать к элементам модели.
- В ModelMetadataProvider при возврате коллекции объектов ModelMetadata для содержащихся свойств запишите контейнер в ModelMetadataProvider, который связан с этими возвращаемыми свойствами. Я пробовал это, но эта возвращенная коллекция метаданных свойств игнорируется в этом случае, а метод FromStringExpression переходит непосредственно к методу GetMetadataForProperty.