tag route net for data asp all asp.net-mvc asp.net-mvc-2 data-annotations

asp.net-mvc - route - select asp-for asp-items



Obtiene el atributo[DisplayName] de una propiedad de manera fuertemente tipada (7)

¡Buen día!

Tengo dicho método para obtener el valor del atributo [DisplayName] de una propiedad (que se adjunta directamente o con 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 nombre de campo como cadena
  • No funciona si quiero obtener la propiedad de una propiedad

¿Es posible superar ambos problemas utilizando lambdas, algo así como lo que tenemos en ASP.NET MVC:

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

Actualizar

Aquí hay una versión actualizada y comprobada de la solución BuildStarted . Se modifica para usar el atributo DisplayName (puede volver a modificar el atributo Display si lo usa). Y corrigió errores menores para obtener el atributo de 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; }


Encontré otro bonito fragmento de código here , y lo modifiqué 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)


Estoy totalmente de acuerdo con la solución BuildStarted provista. Lo único que cambiaría es que ExtensionsMethode no es compatible con las traducciones. Para apoyar esto es simple se necesita un cambio menor. Hubiera puesto esto 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://.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; }


Hago un pequeño cambio porque estás usando recursos para obtener 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://.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; }

Happy Coding


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 de todos los tipos). Esto significa que estará disponible en cualquier objeto y no solo en su modelo. No es realmente recomendable, pero es agradable por su sintaxis concisa.

El segundo método requiere que pases el Tipo de modelo que es, lo que ya estás haciendo, pero como un parámetro. Este método es necesario para definir el tipo a través de Generics porque Func lo espera.

Estos son los métodos para que usted pueda verificar.

Método de extensión estático para 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://.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 del tipo: el mismo código que el anterior solo una llamada diferente

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

La razón por la que no puedes usar Something.GetDisplayName(t => t.Name) por sí solo es porque en el motor Razor estás pasando un objeto instanciado de HtmlHelper<TModel> lo que el primer método requiere una instancia objeto: esto solo es necesario para que el compilador infiera qué tipos pertenecen a qué 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://.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; }


Otro fragmento de código con el código .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)


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 como solicitado:

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


Tarde en el juego, pero ...

Creé un método de ayuda utilizando ModelMetadata como @Daniel mencionado 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!"