如何在 MVC 中 运行 时自定义 Display 和 Required 属性

How to customize Display and Required attributes at run-time in MVC

我想在 razor 视图中创建一个带有动态标签的模型,该标签在运行时设置,但基于使用字符串格式的资源文件中的字符串。

假设我有一个简单的模型,只有一个 属性

public class Simple
{
    [Display(ResourceType = (typeof(Global)), Name = "UI_Property1")]
    [Required(ErrorMessageResourceType = (typeof(Global)), ErrorMessageResourceName = "ERROR_Required")]
    [StringLength(40, ErrorMessageResourceType = (typeof(Global)), ErrorMessageResourceName = "ERROR_MaxLength")]
    public string Property1{ get; set; }
}

并且资源文件有以下字符串

UI_Property1       {0}
ERROR_Required     Field {0} is required.
ERROR_MaxLength    Maximum length of {0} is {1}

我想在剃刀视图中做这样的事情

@Html.LabelFor(m => m.Property1, "xyz", new { @class = "control-label col-sm-4" })

并且生成的视图会将字段标签显示为 'xyz',并且值 'xyz' 也会显示在从服务器模型验证返回的验证消息中。

我一直在寻找各种方法来做这件事,但没有成功。我调查了覆盖 DisplayAttribute 但这是密封的 class.

我还查看了覆盖 DisplayName 属性,但无法通过所需的验证消息正确获取该属性。另外,我不确定如何将动态文本注入到我认为需要在属性构造函数中完成的属性中。

我也考虑过编写自定义 DataAnnotationsModelMetadataProvider,但看不到使用它来实现我想要的东西的方法。这可能是因为我缺乏编码技能。

'xyz' 字符串将来自 web.config 文件中的设置,不需要在 LabelFor 命令中注入,但如果更有意义,可以在其他地方注入。

如果有人能告诉我如何实现这一目标,那就太好了。

我找到了这个post

Is it valid to replace DataAnnotationsModelMetadataProvider and manipulate the returned ModelMetadata

这让我找到了如下解决方案:

我在我的网络配置中添加了一个自定义部分

  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="labelTranslations" type="AttributeTesting.Config.LabelTranslatorSection" />
    ... other sections here
  </configSections>

  <labelTranslations>
    <labels>
      <add label=":Customer:" translateTo="Customer Name" />
      <add label=":Portfolio:" translateTo="Portfolio Name" />
      <add label=":Site:" translateTo="Site Name" />
    </labels>
  </labelTranslations>

用于处理自定义部分的 class 加载要翻译的标签

public class LabelElement : ConfigurationElement
{
    private const string LABEL = "label";
    private const string TRANSLATE_TO = "translateTo";

    [ConfigurationProperty(LABEL, IsKey = true, IsRequired = true)]
    public string Label
    {
        get { return (string)this[LABEL]; }
        set { this[LABEL] = value; }
    }

    [ConfigurationProperty(TRANSLATE_TO, IsRequired = true)]
    public string TranslateTo
    {
        get { return (string)this[TRANSLATE_TO]; }
        set { this[TRANSLATE_TO] = value; }
    }

}

[ConfigurationCollection(typeof(LabelElement))]
public class LabelElementCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new LabelElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((LabelElement)element).Label;
    }

    public LabelElement this[string key]
    {
        get
        {
            return this.OfType<LabelElement>().FirstOrDefault(item => item.Label == key);
        }
    }
}

public class LabelTranslatorSection : ConfigurationSection
{
    private const string LABELS = "labels";

    [ConfigurationProperty(LABELS, IsDefaultCollection = true)]
    public LabelElementCollection Labels
    {
        get { return (LabelElementCollection)this[LABELS]; }
        set { this[LABELS] = value; }
    }
}

然后翻译人员使用自定义部分将给定标签翻译成翻译版本(如果存在),否则 returns 标签

public static class Translator
{
    private readonly static LabelTranslatorSection config =
        ConfigurationManager.GetSection("labelTranslations") as LabelTranslatorSection;

    public static string Translate(string label)
    {
        return config.Labels[label] != null ? config.Labels[label].TranslateTo : label;
    }
}

然后我编写了一个自定义元数据提供程序,它根据翻译版本修改显示名称

public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(
                             IEnumerable<Attribute> attributes,
                             Type containerType,
                             Func<object> modelAccessor,
                             Type modelType,
                             string propertyName)
    {

        // Call the base method and obtain a metadata object.
        var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

        if (containerType != null)
        {
            // Obtain informations to query the translator.
            //var objectName = containerType.FullName;
            var displayName = metadata.GetDisplayName();

            // Update the metadata from the translator
            metadata.DisplayName = Translator.Translate(displayName);
        }

        return metadata;
    }
}

之后一切正常,标签和验证消息都使用翻译版本。我使用了标准的 LabelFor 助手,没有进行任何修改。

资源文件是这样的

ERROR_MaxLength   {0} can be no more than {1} characters long   
ERROR_Required    {0} is a required field   
UI_CustomerName   :Customer:    
UI_PortfolioName  :Portfolio:   
UI_SiteName       :Site: