Asp.net-Mvc

以強類型方式獲取屬性的 [DisplayName] 屬性

  • March 29, 2011

再會!

我有這樣的方法來獲取[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;
   }
}

它有效,但有兩個缺點:

  • 它需要欄位名稱作為字元串
  • 如果我想獲得財產的財產,它不起作用

是否可以使用 lambdas 來克服這兩個問題,就像我們在 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;
}

有兩種方法可以做到這一點:

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

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

第一個是通過為任何 TModel(所有類型)編寫通用擴展方法來工作的。這意味著它將可用於任何對象,而不僅僅是您的模型。不是很推薦,但很好,因為它的語法簡潔。

第二種方法要求您傳入模型的類型 - 您已經在執行此操作,但將其作為參數。這個方法需要通過泛型定義類型,因為 Func 需要它。

以下是您檢查的方法。

所有對象的靜態擴展方法

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;



}

引用自:https://stackoverflow.com/questions/5474460