Obtener el atributo [DisplayName] de una propiedad de forma fuertemente escrita
¡Buenos días!
He dicho método para obtener el valor del atributo [DisplayName]
de una propiedad (que se adjunta directamente o utilizando el atributo [MetadataType]
). Lo uso en casos raros donde necesito obtener [DisplayName]
en el código del controlador.
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;
}
}
Funciona, pero tiene dos inconvenientes:
- Requiere el nombre del campo como cadena
- No funciona si quiero obtener la propiedad de una propiedad
¿Es posible superar ambos problemas usando lambdas, algo como lo que tenemos en ASP.NET MVC:
Html.LabelFor(m => m.Property.Can.Be.Very.Complex.But.Strongly.Typed);
Update
Aquí hay una versión actualizada y comprobada de la solución BuildStarted. Se modifica para usar el atributo DisplayName
(puede modificar de nuevo al atributo Display
si lo usa). Y se corrigieron errores menores para obtener el atributo de las propiedades anidadas.
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;
}
7 answers
Hay dos maneras de hacer esto:
Models.Test test = new Models.Test();
string DisplayName = test.GetDisplayName(t => t.Name);
string DisplayName = Helpers.GetDisplayName<Models.Test>(t => t.Name);
El primero funciona en virtud de escribir un método de extensión genérico para cualquier tModel (que es todos los tipos). Esto significa que estará disponible en cualquier objeto y no solo en su modelo. No es realmente recomendable, pero agradable debido a su sintaxis concisa.
El segundo método requiere que pase el tipo del modelo que es, lo que ya está haciendo, pero como parámetro. Este método es necesario para definir el tipo a través de genéricos porque Func lo espera.
Aquí están los métodos para que usted compruebe hacia fuera.
Método de extensión estática a todos los objetos
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;
}
Firma para el método específico de tipo-mismo código que el anterior solo llamada diferente
public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { }
La razón por la que no puede usar Something.GetDisplayName(t => t.Name)
por sí solo es porque en el motor de Razor está pasando un objeto instanciado de HtmlHelper<TModel>
, por lo que el primer método requiere un objeto instanciado, esto solo es necesario para que el compilador infiera qué tipos pertenecen a cuál nombre genérico.
Actualización con propiedades recursivas
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-30 15:16:15
Tarde para el juego, pero...
Creé un método de ayuda usando ModelMetadata como @ Daniel mencionó y pensé en compartirlo:
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;
}
Ejemplo de uso:
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!"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-04 16:39:43
Solo haz esto:
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;
}
}
}
Ejemplo según lo solicitado:
var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);
var propFriendlyNames = props.Select(x => x.GetDisplayName());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 18:04:33
Estoy totalmente de acuerdo con la solución BuildStarted proporcionada. Lo único que cambiaría es que el ExtensionsMethode no admite traducciones. Para apoyar esto es simple cambio menor es necesario. Lo habría incluido en los comentarios, pero no tengo suficientes puntos para hacerlo. Busque la última línea en el método.
El Método De Extensión
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-03 06:45:50
Hago un Pequeño cambio es que está utilizando recursos para obtener el 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;
}
Feliz Codificación
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-25 12:26:34
He encontrado otro buen fragmento de código aquí , y lo he modificado ligeramente para el propósito' 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;
}
Y usos
GetDisplayName<ModelName, string>(i => i.PropertyName)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-15 17:08:11
Otro fragmento de código con code. Net se usa para realizar esto
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-15 15:20:49