0

Introduction

I have a class, which has properties localized through data annotations by a resource file, like this:

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

Where ResxFile is a .resx file, and I'm using Name = nameof(ResxFile.SomeProperty) to get the name property of the resource file row (to make it strongly typed), and ResourceType = typeof(ResxFile) to indicate which is the resource file to use.

In my ResxFile, for the previous example, I would have something like:

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

And in this way, for example, I can bind my class to a grid, and the column names will be localized according to the content of the resource file.

Question

I'm working with a kind of dynamic mapping, where I use the property names of my classes, and in general I get them with something like this: string propertyName = typeof(MyClassName).GetProperty(myPropertyName).Name

In this case, what I need, is the localized name assigned to that property, according to the resource file. To be more clear: string localizedPropertyName = typeof(MyClassName).GetProperty(myPropertyName).SomeMagic(); where localizedPropertyName would be "Some property localized"

I've been looking in CustomAttributes, but I only could get display name attributes, and some types, and that lands me in another job, which is invoke the resource file to get the value of a name.

I'm using .Net Framework 4.7.

Thanks in advance!

2 Answers 2

3

Finally, I found a solution on my own.

The problem

Then, letting a clear context, what we have is just a class (from which we can extract its type), and a PropertyName on a string, and what we want is the the localized DisplayName of that property of that class, according to a Resource File assigned on its decoration.

Let's suppose some elements to start. We have the class MyClass, which has a property called MyProperty, and which will be localized with the resource file MyResx:

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

The resource file MyResx, has some localized string for the name MyProperty, and will look like this:

How MyResx will look like

The solution

// 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);

Extra

I got a lot of important information from the Microsoft reference about the NamedArguments, here: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.customattributedata.namedarguments?view=netcore-3.1

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, that helps me in Blazor to understand the same problem becuase reflection doesn't work for dispalyName and I have to read this value from the resourcetype by using your way!
0

Hopefully this helps you as in the past I have used this method to translate keys in a database. This does not cover the pulling out data from the resource file, but you can either declare [Display] attribute on a property and use the full name as the key or give a static string as the key to use later in the meta data provider.

Add your own meta data providor

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

add the translation key to the prop

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

Add this to application start up

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

1 Comment

Hi friend, your solution looks amazing. I was trying to port it to my project, but it was impossible. I'm working in a winforms solution, and my data-annotations system come from System.ComponentModel.DataAnnotations. Thank a lot for your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.