web-dev-qa-db-ja.com

強く型付けされた方法でプロパティの[DisplayName]属性を取得します

良い一日!

プロパティの[DisplayName]属性値(直接または[MetadataType]属性を使用)を取得するためのメソッドがあります。まれに、コントローラーコードで[DisplayName]を取得する必要がある場合に使用します。

public static class MetaDataHelper
{
    public static string GetDisplayName(Type dataType, string fieldName)
    {       
        // First look into attributes on a type and it's parents
        DisplayNameAttribute attr;
        attr = (DisplayNameAttribute)dataType.GetProperty(fieldName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

        // Look for [MetadataType] attribute in type hierarchy
        // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
        if (attr == null)
        {
            MetadataTypeAttribute metadataType = (MetadataTypeAttribute)dataType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
            if (metadataType != null)
            {
                var property = metadataType.MetadataClassType.GetProperty(fieldName);
                if (property != null)
                {
                    attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
                }
            }
        }
        return (attr != null) ? attr.DisplayName : String.Empty;
    }
}

動作しますが、2つの欠点があります。

  • 文字列としてフィールド名が必要です
  • プロパティのプロパティを取得したい場合は機能しません

ASP.NET MVCにあるようなラムダを使用して両方の問題を克服することは可能ですか?

Html.LabelFor(m => m.Property.Can.Be.Very.Complex.But.Strongly.Typed);  

更新

以下は、BuildStartedソリューションの更新および確認済みバージョンです。 DisplayName属性を使用するように変更されています(使用する場合は、Display属性に戻すことができます)。また、ネストされたプロパティの属性を取得するためのマイナーなバグを修正しました。

public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
{
    Type type = typeof(TModel);

    string propertyName = null;
    string[] properties = null;
    IEnumerable<string> propertyList;
    //unless it's a root property the expression NodeType will always be Convert
    switch (expression.Body.NodeType)
    {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expression.Body as UnaryExpression;
            propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
            break;
        default:
            propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
            break;
    }

    //the propert name is what we're after
    propertyName = propertyList.Last();
    //list of properties - the last property name
    properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

    foreach (string property in properties)
    {
        PropertyInfo propertyInfo = type.GetProperty(property);
        type = propertyInfo.PropertyType;
    }

    DisplayNameAttribute attr;
    attr = (DisplayNameAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
    if (attr == null)
    {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null)
        {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null)
            {
                attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.DisplayName : String.Empty;
}
40
artvolk

これを行うには2つの方法があります。

_Models.Test test = new Models.Test();
string DisplayName = test.GetDisplayName(t => t.Name);

string DisplayName = Helpers.GetDisplayName<Models.Test>(t => t.Name);
_

最初の方法は、TModel(すべての型)に汎用の拡張メソッドを書くことで機能します。これは、モデルだけでなく任意のオブジェクトで使用できることを意味します。あまりお勧めしませんが、簡潔な構文であるためニースです。

2番目の方法では、モデルのTypeを渡す必要があります。これは既に実行していますが、代わりにパラメーターとして使用します。このメソッドは、Funcが想定しているため、Genericsを介して型を定義するために必要です。

チェックアウトする方法は次のとおりです。

すべてのオブジェクトへの静的拡張メソッド

_public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression) {

    Type type = typeof(TModel);

    MemberExpression memberExpression = (MemberExpression)expression.Body;
    string propertyName = ((memberExpression.Member is PropertyInfo) ? memberExpression.Member.Name : null);

    // First look into attributes on a type and it's parents
    DisplayAttribute attr;
    attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
    if (attr == null) {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null) {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null) {
                attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.Name : String.Empty;


}
_

型固有のメソッドのシグネチャ-上記と同じコードだけ異なる呼び出し

_public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { }
_

Something.GetDisplayName(t => t.Name)を単独で使用できない理由は、Razorエンジンでは_HtmlHelper<TModel>_のインスタンス化されたオブジェクトを実際に渡しており、これが最初のメソッドがインスタンス化されたオブジェクトを必要とするためです-コンパイラーがどのタイプがどの総称名に属するかを推測するためにのみ必要です。

再帰的なプロパティで更新する

_public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) {

    Type type = typeof(TModel);

    string propertyName = null;
    string[] properties = null;
    IEnumerable<string> propertyList;
    //unless it's a root property the expression NodeType will always be Convert
    switch (expression.Body.NodeType) {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
            var ue = expression.Body as UnaryExpression;
            propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
            break;
        default:
            propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
            break;
    }

    //the propert name is what we're after
    propertyName = propertyList.Last();
    //list of properties - the last property name
    properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

    Expression expr = null;
    foreach (string property in properties) {
        PropertyInfo propertyInfo = type.GetProperty(property);
        expr = Expression.Property(expr, type.GetProperty(property));
        type = propertyInfo.PropertyType;
    }

    DisplayAttribute attr;
    attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

    // Look for [MetadataType] attribute in type hierarchy
    // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
    if (attr == null) {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null) {
            var property = metadataType.MetadataClassType.GetProperty(propertyName);
            if (property != null) {
                attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
            }
        }
    }
    return (attr != null) ? attr.Name : String.Empty;



}
_
39
Buildstarted

ゲームに遅れて、しかし...

前述の@DanielのようなModelMetadataを使用してヘルパーメソッドを作成し、共有したいと思いました。

public static string GetDisplayName<TModel, TProperty>(
      this TModel model
    , Expression<Func<TModel, TProperty>> expression)
{
    return ModelMetadata.FromLambdaExpression<TModel, TProperty>(
        expression,
        new ViewDataDictionary<TModel>(model)
        ).DisplayName;
}

使用例:

Models

public class MySubObject
{
    [DisplayName("Sub-Awesome!")]
    public string Sub { get; set; }
}

public class MyObject
{
    [DisplayName("Awesome!")]
    public MySubObject Prop { get; set; }
}

Use

HelperNamespace.GetDisplayName(Model, m => m.Prop) // "Awesome!"
HelperNamespace.GetDisplayName(Model, m => m.Prop.Sub) // "Sub-Awesome!"
31
JesseBuesking

これを行うだけです:

using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace yournamespace
{
    public static class ExtensionMethods
    {
        public static string GetDisplayName(this PropertyInfo prop)
        {
            if (prop.CustomAttributes == null || prop.CustomAttributes.Count() == 0)
                return prop.Name;

            var displayNameAttribute = prop.CustomAttributes.Where(x => x.AttributeType == typeof(DisplayNameAttribute)).FirstOrDefault();

            if (displayNameAttribute == null || displayNameAttribute.ConstructorArguments == null || displayNameAttribute.ConstructorArguments.Count == 0)
                return prop.Name;

            return displayNameAttribute.ConstructorArguments[0].Value.ToString() ?? prop.Name;
        }
    }
}

要求された例:

var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);

var propFriendlyNames = props.Select(x => x.GetDisplayName());
4
Joe

BuildStartedが提供するソリューションに完全に同意します。私が変更する唯一のことは、ExtensionsMethodeが翻訳をサポートしていないことです。これをサポートするには、簡単な小さな変更が必要です。私はこれをコメントに入れたでしょうが、そうするのに十分なポイントがありません。メソッドの最後の行を探します。

拡張方法

public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression)
{
        Type type = typeof(TModel);
        IEnumerable<string> propertyList;

        //unless it's a root property the expression NodeType will always be Convert
        switch (expression.Body.NodeType)
        {
            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
                var ue = expression.Body as UnaryExpression;
                propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
                break;
            default:
                propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
                break;
        }

        //the propert name is what we're after
        string propertyName = propertyList.Last();
        //list of properties - the last property name
        string[] properties = propertyList.Take(propertyList.Count() - 1).ToArray();

        Expression expr = null;
        foreach (string property in properties)
        {
            PropertyInfo propertyInfo = type.GetProperty(property);
            expr = Expression.Property(expr, type.GetProperty(property));
            type = propertyInfo.PropertyType;
        }

        DisplayAttribute attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

        // Look for [MetadataType] attribute in type hierarchy
        // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
        if (attr == null)
        {
            MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
            if (metadataType != null)
            {
                var property = metadataType.MetadataClassType.GetProperty(propertyName);
                if (property != null)
                {
                    attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
                }
            }
        }
        //To support translations call attr.GetName() instead of attr.Name
        return (attr != null) ? attr.GetName() : String.Empty;
 }
2
Bosken85

別の素敵なコードスニペット here を見つけました。「DisplayName」の目的のために少し変更しました

    public static string GetDisplayName<TSource, TProperty>(Expression<Func<TSource, TProperty>> expression)
    {
        var attribute = Attribute.GetCustomAttribute(((MemberExpression)expression.Body).Member, typeof(DisplayNameAttribute)) as DisplayNameAttribute;

        if (attribute == null)
        {
            throw new ArgumentException($"Expression '{expression}' doesn't have DisplayAttribute");
        }

        return attribute.DisplayName;
    }

そして使用法

GetDisplayName<ModelName, string>(i => i.PropertyName)
1
Shuvo Amin

リソースを使用してDisplayNameを取得しているので、少し変更します

    public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression)
  {

     string _ReturnValue = string.Empty;

     Type type = typeof(TModel);

     string propertyName = null;
     string[] properties = null;
     IEnumerable<string> propertyList;
     //unless it's a root property the expression NodeType will always be Convert
     switch (expression.Body.NodeType)
     {
        case ExpressionType.Convert:
        case ExpressionType.ConvertChecked:
           var ue = expression.Body as UnaryExpression;
           propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property
           break;
        default:
           propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1);
           break;
     }

     //the propert name is what we're after
     propertyName = propertyList.Last();
     //list of properties - the last property name
     properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties

     Expression expr = null;
     foreach (string property in properties)
     {
        PropertyInfo propertyInfo = type.GetProperty(property);
        expr = Expression.Property(expr, type.GetProperty(property));
        type = propertyInfo.PropertyType;
     }

     DisplayAttribute attr;
     attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();

     // Look for [MetadataType] attribute in type hierarchy
     // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class
     if (attr == null)
     {
        MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
        if (metadataType != null)
        {
           var property = metadataType.MetadataClassType.GetProperty(propertyName);
           if (property != null)
           {
              attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault();
           }
        }
     }

     if (attr != null && attr.ResourceType != null)
        _ReturnValue = attr.ResourceType.GetProperty(attr.Name).GetValue(attr).ToString();
     else if (attr != null)
        _ReturnValue = attr.Name;

     return _ReturnValue;
  }

ハッピーコーディング

1
Fernando

コード.Netを含む別のコードスニペットは、それ自体を使用してこれを実行します

public static class WebModelExtensions
{
    public static string GetDisplayName<TModel, TProperty>(
      this HtmlHelper<TModel> html, 
      Expression<Func<TModel, TProperty>> expression)
    {
        // Taken from LabelExtensions
        var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);

        string displayName = metadata.DisplayName;
        if (displayName == null)
        {
            string propertyName = metadata.PropertyName;
            if (propertyName == null)
            {
                var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
                displayName = ((IEnumerable<string>) htmlFieldName.Split('.')).Last<string>();
            }
            else
                displayName = propertyName;
        }

        return displayName;
    }
}
// Usage
Html.GetDisplayName(model => model.Password)
0
Siarhei Kuchuk