从使用资源文件的 class 属性 获取本地化的显示名称属性

Get localized display name attribute from a class property which use a resource file

简介

我有一个 class,它的属性通过资源文件的数据注释进行了本地化,如下所示:

[Display(Name = nameof(ResxFile.SomeProperty), ResourceType = typeof(ResxFile)]
public string SomeProperty { get; set; }

其中 ResxFile 是一个 .resx 文件,我正在使用 Name = nameof(ResxFile.SomeProperty) 获取资源文件行的名称 属性(使其成为强类型),并且ResourceType = typeof(ResxFile) 表示使用哪个资源文件。

在我的 ResxFile 中,对于前面的例子,我会有这样的东西:

Name            | Value
------------------------------------------
SomeProperty    | Some property localized

并且通过这种方式,例如,我可以将我的 class 绑定到一个网格,列名将根据资源文件的内容进行本地化。

问题

我正在使用一种动态映射,我在其中使用 class 的 属性 名称,通常我会用这样的方式获取它们:string propertyName = typeof(MyClassName).GetProperty(myPropertyName).Name

在这种情况下,我需要的是根据资源文件分配给 属性 的本地化名称。更清楚地说:string localizedPropertyName = typeof(MyClassName).GetProperty(myPropertyName).SomeMagic(); 其中 localizedPropertyName 将是“部分 属性 本地化”

我一直在寻找 CustomAttributes,但我只能获得显示名称属性和某些类型,这使我从事另一项工作,即调用资源文件以获取 a 的值名字.

我正在使用 .Net Framework 4.7。

提前致谢!

希望这对您有所帮助,因为我过去曾使用这种方法来翻译数据库中的键。这不包括从资源文件中提取数据,但您可以在 属性 上声明 [Display] 属性并使用全名作为键,或者提供一个静态字符串作为键以备后用在元数据提供者中。

添加您自己的元数据提供商

public class MyMetadataProvider : DataAnnotationsModelMetadataProvider
{
    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
        Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        var metadata = new ModelMetadata(this, containerType, modelAccessor, modelType, propertyName);
        //Do what ever you want here to translate either by the property name or the display attribute key

        if (propertyName != null)
        {
            var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
            if (displayAttribute != null)
            {                       
                //Translate using the key you provided before however you like
                metadata.DisplayName = TranslateFunction(displayAttribute.Name);
            }
        }
        return metadata;
    }
}

将翻译键添加到道具中

[Display(Name = "ResourceKey")]
public string Something { get; set; }

将此添加到应用程序启动

protected void Application_Start(object sender, EventArgs e)
{
    ModelMetadataProviders.Current = new MyMetadataProvider();
}

最后,我自己找到了解决办法。

问题

然后,明确上下文,我们得到的只是一个class(从中我们可以提取它的类型)和一个字符串上的PropertyName,我们想要的是class 的 属性 的本地化 DisplayName,根据装饰上分配的 Resource File

让我们假设一些元素开始。我们有 class MyClass,它有一个名为 MyProperty 的 属性,它将与资源文件一起本地化 MyResx:

public class MyClass
{
    private string myProperty;
    [Display(Name = nameof(MyResx.MyProperty), ResourceType = typeof(MyResx))]
    public string MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}

资源文件 MyResx,有一些本地化的名称字符串 MyProperty,看起来像这样:

解决方法

// We start with the class type, and the property name on a string
Type classType = typeof(MyClass);
string nameOfTheProperty = "MyProperty";

/* Now we get the MemberInfo of our property, wich allow us to get the
 * property metadata, where is the information we are looking for. */
MemberInfo propertyMetadata = classType.GetProperty(nameOfTheProperty);

/* The decorations we used, are "Custom Attributes". Now we get those 
 * attributes from our property metadata: */
var customAttributes = CustomAttributeData.GetCustomAttributes(propertyMetadata).FirstOrDefault();

/* If we pay attention to our decoration, we defined "Name = nameof(MyResx.MyProperty)"
 * and "ResourceType = typeof(MyResx))", so, what we are looking for from our custom 
 * attribures are those members, Name and ResourceType: */
var customAttributeName = customAttributes.NamedArguments.FirstOrDefault(n => n.MemberName == "Name");
var name = (customAttributeName != null) ? (string)customAttributeName.TypedValue.Value : null;

var customAttributeResourceType = customAttributes.NamedArguments.FirstOrDefault(n => n.MemberName == "ResourceType");
var resourceType = (customAttributeResourceType != null) ? (Type)customAttributeResourceType.TypedValue.Value : null;

/* Now, having the resource file from the decoration, we just create an instance to
 * use it: */
var decorationResx = new ComponentResourceManager(resourceType);

// And finally, from our resource file, we get our localized display name
string localizedAttribute = decorationResx.GetString(name);

额外

我从 Microsoft 参考资料中获得了很多关于 NamedArguments 的重要信息,此处:https://docs.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata.namedarguments?view=netcore-3.1