I have a class that defines data annotations at class level. The meta data class has custom attributes associated with it, along with the usual DisplayName, DisplayFormat etc.
public class BaseMetaData
{
[DisplayName("Id")]
public object Id { get; set; }
[DisplayName("Selected")]
[ExportItem(Exclude = true)]
public object Selected { get; set; }
}
[MetadataType(typeof(BaseMetaData))]
public class BaseViewModel
{
public int Id { get; set; }
public bool Selected { get; set; }
Given a type T, how can I retrieve the custom attributes from the meta data class? The attempt below would not work as the metadata properties are from the BaseViewModel rather than the BaseMetaData class.
Needs to work generically i.e. can't do typeof(BaseMetaData).GetProperty(e.PropertyName). Wondering if there is a way of getting the MetadataType from the class then it would make it possible.
var type = typeof (T);
var metaData = ModelMetadataProviders.Current.GetMetadataForType(null, type);
var propertMetaData = metaData.Properties
.Where(e =>
{
var attribute = type.GetProperty(e.PropertyName)
.GetCustomAttributes(typeof(ExportItemAttribute), false)
.FirstOrDefault() as ExportItemAttribute;
return attribute == null || !attribute.Exclude;
})
.ToList();
Found a solution by using the type of MetadataTypeAttribute to get the custom attributes.
var type = typeof (T);
var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
.OfType<MetadataTypeAttribute>().FirstOrDefault();
var metaData = (metadataType != null)
? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
: ModelMetadataProviders.Current.GetMetadataForType(null, type);
var propertMetaData = metaData.Properties
.Where(e =>
{
var attribute = metaData.ModelType.GetProperty(e.PropertyName)
.GetCustomAttributes(typeof(ExportItemAttribute), false)
.FirstOrDefault() as ExportItemAttribute;
return attribute == null || !attribute.Exclude;
})
.ToList();
Have you tried just doing this,
public class BaseViewModel
{
[DisplayName("Id")]
public int Id { get; set; }
[DisplayName("Selected")]
[ExportItem(Exclude = true)]
public bool Selected { get; set; }
}
You can then use a variation of your code,
var type = typeof(T);
var propertyMetaData = type.GetProperties()
.Select(property =>
property.GetCustomAttributes(typeof(ExportItemAttribute), false)
.FirstOrDefault() as ExportItemAttribute)
.Where(attribute => attribute == null || !attribute.Exclude)
.ToList();
I was looking for something similar, and looking at the class MetadataTypeAttribute, I realice that it stores the type of the Metdata class. Inside that class, you can have get/set properties, or just fields (get/set properties are defined in one partial class, like autogenerated models in MVC), so, I read the fields within that metadata class, then get the atributes for the field. Code is:
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
namespace PruebaAtributos
{
// Podemos ver la definición de 'MetaDataType' en:
// https://referencesource.microsoft.com/#System.ComponentModel.DataAnnotations/DataAnnotations/MetadataTypeAttribute.cs,fb9a5881152a1584,references
[MetadataType(typeof(ProgramMetadata))]
partial class Program
{
// Campos de la clase
public int Id { get; set; }
public string Nombre { get; set; }
public string Puesto { get; set; }
static void Main(string[] args)
{
Type t = typeof(Program);
// Atributos de la clase
Console.WriteLine("--- Atributos de clase: ");
Attribute[] attrs = Attribute.GetCustomAttributes(t);
foreach (Attribute at in attrs)
{
Console.WriteLine(at.GetType().Name);
if (at is MetadataTypeAttribute mdt)
{
// Nos interesa la información que contiene 'MetadataType'
Console.WriteLine($"--- Campos de {mdt.GetType().Name}:");
// Obtenemos las propiedades de la clase asociada con metadata type
var fields = mdt.MetadataClassType.GetFields();
foreach (FieldInfo fi in fields)
{
// Y mostramos los atributos asociados a cada uno de sus campos
var cas = fi.GetCustomAttributes(); // ca = Custom Attributes
Console.WriteLine($" {fi.Name}.");
Console.WriteLine($" attributos: {string.Join(", ", cas.Select(a => a.GetType().Name))}");
// Ahora consultamos la propiedad que deseamos de cada atributo conocido:
// Para consultar un attributo específico:
//DisplayAttribute da = (DisplayAttribute)ca.FirstOrDefault(a => a.GetType() == typeof(DisplayAttribute));
//if (da != null)
//{
// Console.WriteLine($" {da.GetType().Name}: {da.Name}");
//}
string desc;
foreach (var fa in cas) // fa = Field Attribute
{
if (fa is ExportarAttribute exp)
{
// Conocemos las propiedades específicas de este
desc = $"{exp.GetType().Name}.exportar: {exp.exportar}";
}
else if (fa is MostrarAUsuario mau)
{
desc = $"{mau.GetType().Name}.mostrar: {mau.mostrar}";
}
else if (fa is DisplayAttribute da)
{
desc = $"{da.GetType().Name}.Name: {da.Name}";
}
else
{
desc = fa.GetType().Name;
}
Console.WriteLine($" {desc}");
}
}
}
}
}
}
// Attributos personalizados
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
class MostrarAUsuario : Attribute
{
public readonly bool mostrar;
public MostrarAUsuario(bool mostrar = true)
{
this.mostrar = mostrar;
}
};
class ExportarAttribute : Attribute
{
public readonly bool exportar;
public ExportarAttribute(bool exportar = true)
{
this.exportar = exportar;
}
}
public class ProgramMetadata
{
// Display pertenece a MVC: System.ComponentModel.DataAnnotations
[Display(Name = "Identificador"), MostrarAUsuario(false), Exportar(false), Phone]
public int Id;
[Display(Name = "Nombre completo"), MostrarAUsuario]
public int Nombre;
[Display(Name = "Puesto de trabajo"), Exportar]
public int Puesto;
}
}
Result I see is:
Based on the other answers, i'm successed to get the DisplayName attribute from MetadataType class
in this way:
var metadataType = type.GetCustomAttributes(typeof(MetadataTypeAttribute), true)
.OfType<MetadataTypeAttribute>().FirstOrDefault();
var metaData = (metadataType != null)
? ModelMetadataProviders.Current.GetMetadataForType(null, metadataType.MetadataClassType)
: ModelMetadataProviders.Current.GetMetadataForType(null, type);
List<string> propertyList = metaData.Properties.
Select(x => x.DisplayName).ToList();