使用自定义功能覆盖 MVC 模型显示名称注释

Override MVC Model Display Name Annotation with Custom Functionality

我有以下从键值 XML 文件中读取的方法。我传入一个键并返回一个值,该值用于显示在我的视图中。

public static class TextManager
{
    public static string GetValue(string key)
    {
        string returnVal = null; 
        XmlSerializer serializer = new XmlSerializer(typeof(Entries));
        string path = HttpContext.Current.Server.MapPath("/App_Data/text-key-value.xml");
        if (File.Exists(path))
        {
            Entries entries = (Entries)serializer.Deserialize(File.OpenRead(path));
            var entry = entries.Where(u => u.Key == key).FirstOrDefault();
            if (entry != null)
            {
                returnVal = entry.Value;
            }
        }
        return returnVal;
    }
}

基本上我希望能够在我的模型 class 中使用此方法作为数据注释,它会直接从我的站点文本文件中提取并设置为显示名称 属性。

比如我想替换

[Display(Name = "Reference Code")]
public string ReferenceCode { get; set; }

有了这个

[DisplaySiteText("ReferenceCodeKey")]
public string ReferenceCode { get; set; }

DisplaySiteText 会将字符串引用 "ReferenceCodeKey" 传递给 GetValue 方法,将引用归档到文件中,然后将标准显示名称属性设置为文件中的任何内容。

我如何创建自己的自定义模型注释来执行此操作,我过去通过创建继承自 ValidationAttribute 的 class 来编写自定义验证注释,但我认为这行不通在这种情况下。

也许您的 DisplaySiteText 属性可以继承自 Display 属性并使用您的助手 class 设置名称。像这样:

public class DisplaySiteTextAttribute : DisplayAttribute
{
    public DisplaySiteTextAttribute(string key)
    {
        Name = TextManager.GetValue(key);
    }
}

您可以为此目的继承DisplayNameAttribute

public class DisplaySiteTextAttribute : DisplayNameAttribute
{
    private string _key;

    public DisplaySiteTextAttribute(string key)
    {
        _key = key;
    }

    public override string DisplayName
    {
        get
        {
            return TextManager.GetValue(_key);
        }
    }
}

有几个自定义模型元数据的选项:

  • 自定义框架提供元数据的方式。 (创建ModelMedatadaProvider)
  • 创建新的元数据属性。 (实施IMetadataAware)
  • 修改现有属性。 (派生现有属性。)

第三个选项已经在其他答案中讨论过了。在此post,我将分享第一个和第二个选项。

选项 1 - 自定义框架提供元数据的方式

您可以在不更改属性的情况下更改获取显示文本的逻辑。

事实上,ModelMetaDataProvider 负责获取模型的 mete 数据,包括属性的显示文本。因此,作为一种选择,您可以保持 Display 属性不变,而是创建一个新的模型元数据提供程序和来自不同来源的 return 属性 元数据。

为此,您可以通过从 DataAnnotationsModelMetadataProvider 派生来创建新的元数据提供程序。然后覆盖 GetMetadataForProperty 并调用 base,以获取元数据。然后根据您的逻辑从您的文本管理器中读取更改 DisplayName

您还需要在 App_Start 中将新的元数据提供程序注册为 ModelMetadataProviders.Current

using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
public class MyCustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
        Type containerType,
        PropertyDescriptor propertyDescriptor)
    {
        var metadata = base.GetMetadataForProperty(modelAccessor, 
            containerType, propertyDescriptor);
        var display = propertyDescriptor.Attributes
            .OfType<DisplayAttribute>().FirstOrDefault();
        if (display != null)
        {
            metadata.DisplayName = TextManager.GetValue(display.Name);
        }
        return metadata;
    }
}

然后在Application_Start()中注册:

ModelMetadataProviders.Current = new MyCustomModelMetadataProvider();

有关详细信息,请查看 ASP.NET MVC 源中的 DataAnnotationsModelMetadataProvider.cs 源代码。

This approach is useful when you want to change the way that you provide metadata for model. For example when you want to load display name and description from an external file rather than resources, without changing existing attributes.

选项 2 - 创建新的元数据属性

创建元数据感知属性的另一种标准解决方案是创建属性并实现 IMetadataAware 接口。然后在 OnMetadataCreated 的实现中,您可以轻松设置 metadata 的属性。

此方法不需要注册新的元数据提供程序。默认元数据提供程序支持此方法,对于创建新的元数据感知属性很有用:

using System;
using System.Web.Mvc;
public class CustomMetadataAttribure : Attribute, IMetadataAware
{
    public string Key { get; set; }
    public CustomMetadataAttribure(string key) => this.Key = key;
    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.DisplayName = TextManager.GetValue(this.Key);
    }
}

This approach is useful when you want to extend metadata attributes and add a few more attributes. For example when you want to add some attributes to control rendering. You can set ModelMetadata properties or add some new values to its AdditionalValues dictionary.