良い一日!
プロパティの[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;
}
これを行うには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;
}
_
ゲームに遅れて、しかし...
前述の@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!"
これを行うだけです:
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());
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;
}
別の素敵なコードスニペット 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)
リソースを使用して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;
}
ハッピーコーディング
コード.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)