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

Tro*_*ven 3 c# asp.net-mvc modelstate data-annotations asp.net-mvc-4

我从键值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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

例如我要替换

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

有了这个

[DisplaySiteText("ReferenceCodeKey")]
public string ReferenceCode { get; set; }
Run Code Online (Sandbox Code Playgroud)

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

我如何创建自己的自定义模型注释来实现此目的,过去我通过创建一个继承自ValidationAttribute的类来编写自定义验证注释,但在这种情况下我认为这不起作用。

Ale*_*der 5

您可以DisplayNameAttribute为此目的继承

public class DisplaySiteTextAttribute : DisplayNameAttribute
{
    private string _key;

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

    public override string DisplayName
    {
        get
        {
            return TextManager.GetValue(_key);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Rez*_*aei 5

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

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

第三个选项已在另一个答案中讨论过。在这篇文章中,我将分享第一个和第二个选项。

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

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

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

为此,您可以通过从DataAnnotationsModelMetadataProvider. 然后覆盖GetMetadataForProperty并调用 base,以获取元数据。然后DisplayName通过阅读文本管理器根据您的逻辑进行更改。

您还需要像ModelMetadataProviders.Current中那样注册新的元数据提供程序App_Start

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将其注册到Application_Start()

ModelMetadataProviders.Current = new MyCustomModelMetadataProvider();
Run Code Online (Sandbox Code Playgroud)

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

当您想要更改为模型提供元数据的方式时,此方法很有用。例如,当您想从外部文件而不是资源加载显示名称和描述时,而不更改现有属性。

选项 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当您想要扩展元数据属性并添加更多属性时,此方法很有用。例如,当您要添加一些属性来控制渲染时。您可以设置ModelMetadata属性或向其AdditionalValues字典添加一些新值。