Добавить новое свойство в MVC ModelMetadata

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

Например, вы могли бы иметь:

@ViewData.ModelMetadata.MvvmBound

Я хочу использовать это в редакторе и шаблонах отображения, чтобы добавить атрибуты MVVM к отображаемым элементам HTML.

Я полностью потерян, но вот мои усилия пока:

public class MyModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
    protected new MyModelMetaData CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        ModelMetadata modelMetaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
        MyModelMetaData myMetaData = (MyModelMetaData)modelMetaData;

        return myMetaData;
    }
}

public class MyModelMetaData: ModelMetadata
{
    public MyModelMetaData(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
        : base(provider, containerType, modelAccessor, modelType, propertyName)
    {

    }

    public int MyProperty { get; set; }
}  

Тогда в глобальном asax я использую:

ModelMetadataProviders.Current =new MyModelMetaDataProvider();

Проблема заключается в том, что если я пытаюсь использовать MyProperty в неопознанном представлении, что я и ожидал, поскольку VS не знает о пользовательском классе ModelMetadata.

Я даже не уверен, что это возможно?

1 ответ

Решение

Есть словарь AdditionalValues в ModelMetadata учебный класс. Ссылка здесь

Вы можете добавить значения в этот словарь, используя AdditionalMetadata атрибут, как в:

public class DummyModel
{
    [AdditionalMetadata("MvvmBound", true)]
    public string PropertyA{ get; set; }

    public string PropertyB{ get; set; }
}

И вы можете проверить это в string шаблон редактора, например:

@{
    bool isBound = false;
    if(ViewData.ModelMetadata.AdditionalValues.ContainsKey("MvvmBound"))
    {
        isBound = (bool)ViewData.ModelMetadata.AdditionalValues["MvvmBound"];    
    }
}

//use isBound to set some attributes in the html element

Вы также можете проверить эти атрибуты в шаблоне редактора для DummyModel, что вы можете сделать, получая метаданные для отдельных свойств, как в:

@ModelMetadata.FromLambdaExpression(x => x.PropertyA, ViewData).AdditionalValues["MvvmBound"] 

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

public static class ModelMetadataExtensions
{
    public static bool IsMvvmBound(this ModelMetadata metadata)
    {
        if (!metadata.AdditionalValues.ContainsKey("MvvmBound")) return false;
        return (bool)metadata.AdditionalValues["MvvmBound"];
    }
}

Что позволит вам более чистый код в редакторе шаблонов. Например, в шаблоне редактора для DummyModel выше вы могли бы сделать что-то вроде (не забудьте добавить пространство имен метода расширения в файл Web.config внутри папки /Views):

<h3>WholeModel Is MvvmBound?: @ViewData.ModelMetadata.IsMvvmBound()</h3>
<h3>PropertyA Is MvvmBound?: @ModelMetadata.FromLambdaExpression(x => x.PropertyA, ViewData).IsMvvmBound()</h3>
<h3>PropertyB Is MvvmBound?: @ModelMetadata.FromLambdaExpression(x => x.PropertyB, ViewData).IsMvvmBound()</h3>

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

public class IsMvvmBoundAttribute : Attribute, IMetadataAware 
{
    public IsMvvmBoundAttribute()
    {
        this.IsMvvmBound = true;
    }

    public bool IsMvvmBound { get; set; }

    //This is the single method of IMetadataAware, and what allows us to add the values into the AdditionalValues dictionary
    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues.Add("MvvmBound", this.IsMvvmBound);
    }
}

public class DummyModel
{
    //[AdditionalMetadata("MvvmBound", true)] <- replaced with the new attribute
    [IsMvvmBoundAttribute]
    public string PropertyA{ get; set; }

    public string PropertyB{ get; set; }
}

Так как ModelMetadata.AdditionalValues это Dictionary<string,object>Вы не ограничены хранением простого значения для каждого ключа. Вы можете следовать приведенному выше коду, чтобы создать свой собственный класс, содержащий все необходимые вам свойства, атрибут, который можно использовать для установки каждого из этих значений и сохранения экземпляра этого класса в словаре AdditionalValues, и метод расширения, который извлекает экземпляр этого класса из словаря.

Как видите, вы можете пойти довольно далеко без необходимости ModelMetadata класс или расширение ModelMetadataProvider,

Надеюсь, поможет!

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