向MVC ModelMetadata添加新属性 [英] Add new Property to MVC ModelMetadata

查看:145
本文介绍了向MVC ModelMetadata添加新属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在看到其他方法,例如AdditionalValues,但是我想知道是否有可能最终将模板视图中可用的新属性添加到ModelMetadata对象的情况.

例如,您可能拥有:

@ViewData.ModelMetadata.MvvmBound

我想在Editor和Display模板中使用它来将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类.

我什至不确定这是否可行?

解决方案

ModelMetadata类中有一个词典AdditionalValues. 在此处引用

您可以使用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的编辑器模板中,您可以执行以下操作(请记住,将扩展方法的名称空间添加到/Views文件夹内的Web.config文件中):

<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.AdditionalValuesDictionary<string,object>,所以您不仅限于为每个键存储一个简单值.您可以按照上面的代码创建自己的类,该类包含所需的所有属性,可用于设置每个值的属性,并将该类的实例保存到AdditionalValues字典中,以及用于提取该实例的扩展方法.字典中该类的内容.

如您所见,您可以走得很远,而无需自己的ModelMetadata类或扩展ModelMetadataProvider.

希望有帮助!

I'm seeing alternative approaches to this such as AdditionalValues, but I'm wondering if it's possible to end up in a scenario where you can add a new property to the ModelMetadata object available in template views.

For example you could have:

@ViewData.ModelMetadata.MvvmBound

I want to use this in Editor and Display templates to add MVVM attributes to the HTML elements being rendered.

I'm completely lost, but here are my efforts so far:

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

Then in global asax I use:

ModelMetadataProviders.Current =new MyModelMetaDataProvider();

The problem is if I try to use MyProperty in the view it isn't recognized, which I anticipated because VS isn't aware of the custom ModelMetadata class.

I'm not even sure this is possible?

解决方案

There is a dictionary AdditionalValues in the ModelMetadata class. Ref here

You can add values to that dictionary using the AdditionalMetadata attribute, as in:

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

    public string PropertyB{ get; set; }
}

And you could check those in a string editor template, for example:

@{
    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

You may also want to check those attributes in the editor template for DummyModel, which you can do retrieving the metadata for individual properties as in:

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

There are some downsides of this code as it is. The approach requires to pass the key name to the AdditionalValues dictionary and you also need to cast the value from object to the proper type. You could create your own extension method for retrieving that value from the metadata without the need to manually provide the key name and manually perform a casting:

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

Which would allow you a cleaner code in the editor templates. For example in an editor template for the DummyModel above you could do something like (remember to add the namespace of the extension method to the Web.config file inside the /Views folder):

<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>

You could pair that extension method with your custom attribute extending IMetadataAware so you don't need to hardcode the name of the key for the metadata value every time you use it on a property:

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

Because ModelMetadata.AdditionalValues is a Dictionary<string,object>, you are not limited to storing a simple value per key. You can follow the code above to create you own class holding all the properties that you need, an attribute that you can use to set each of those values and saves an instance of that class to the AdditionalValues dictionary and an extension method that extracts the instance of that class from the dictionary.

As you can see, you can go pretty far without the need for your own ModelMetadata class or extending the ModelMetadataProvider.

Hope it helps!

这篇关于向MVC ModelMetadata添加新属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆