【问题标题】:How to get metadata custom attributes?如何获取元数据自定义属性?
【发布时间】:2014-06-02 14:09:08
【问题描述】:

我有一个在类级别定义数据注释的类。元数据类具有与之关联的自定义属性,以及通常的 DisplayName、DisplayFormat 等。

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; }

给定类型 T,我如何从元数据类中检索自定义属性?下面的尝试不起作用,因为元数据属性来自 BaseViewModel 而不是 BaseMetaData 类。

需要通用工作,即不能执行 typeof(BaseMetaData).GetProperty(e.PropertyName)。想知道是否有一种方法可以从类中获取 MetadataType,那么它将成为可能。

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();

【问题讨论】:

  • 有什么理由不能只用这些属性注释 BaseViewModel 吗?我从关注点分离的角度理解,但这可能有点矫枉过正。
  • 没有理由,如果有可能知道会很有趣!
  • @David 几乎一切皆有可能!!..只是我们需要一些逻辑的味道。

标签: c# reflection data-annotations


【解决方案1】:

通过MetadataTypeAttribute的类型获取自定义属性找到了解决方案。

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();

【讨论】:

  • 你能解释一下你是如何在元数据属性中使用上述代码的吗?
【解决方案2】:

您是否尝试过这样做,

public class BaseViewModel
{
    [DisplayName("Id")]
    public int Id { get; set; }

    [DisplayName("Selected")]
    [ExportItem(Exclude = true)]
    public bool Selected { get; set; }
}

然后您可以使用代码的变体,

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();

【讨论】:

    【解决方案3】:

    我一直在寻找类似的东西,并查看 MetadataTypeAttribute 类,我意识到它存储了 Metdata 类的类型。在该类中,您可以拥有 get/set 属性,或者只是字段(get/set 属性在一个部分类中定义,如 MVC 中的自动生成模型),因此,我读取了该元数据类中的字段,然后获取属性场。代码是:

        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;
            }
        }
    

    我看到的结果是:

    【讨论】:

    • 请注意,只列出公共字段/属性,如果不公开,则不会列出。
    【解决方案4】:

    根据其他答案,我已成功从 MetadataType 类中获取 DisplayName 属性 这样:

    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();
    

    【讨论】:

      【解决方案5】:

      我提供以下解决方案作为替代方案。它适用于任何自定义属性(此示例演示 StringLength)并且速度很快。它基于上面的这个method 和这个method

      MetaDataType 属性和类型类:

      [MetadataType(typeof(ImportSetMetaData))]
      public partial class ImportSet
      {
      }
      
      public class ImportSetMetaData
      {
          [StringLength(maximumLength: 32)]
          public string Segment { get; set; }
      

      扩展方法:

      public static class Extension
      {
          private static int? GetMaxLength<T>(Expression<Func<T, string>> propertyExpression)
          {
              int? result = GetPropertyAttributeValue<T, string, StringLengthAttribute, int?>
                      (propertyExpression, attr => attr.MaximumLength);
              return result;
          }   
      
          public static int? GetMaxLength<T>(this T instance, Expression<Func<T, string>> propertyExpression)
          {
              return GetMaxLength<T>(propertyExpression);
          }
      
          private static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>
              (Expression<Func<T, TOut>> propertyExpression, Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
          {
              var expression = (MemberExpression)propertyExpression.Body;
              string mName = expression.Member.Name;
              Type type = typeof(T);
              MemberInfo member = type.GetMember(mName).FirstOrDefault();
              var attr = member.GetCustomAttribute<TAttribute>(inherit: true);
              if (attr != null)
              {
                  return valueSelector(attr);
              }
              else
              {
                  var mdTypeAttr = (MetadataTypeAttribute)type.GetCustomAttribute<MetadataTypeAttribute>(inherit: true);
                  type = mdTypeAttr.MetadataClassType;
                  member = type.GetMember(mName).FirstOrDefault();
                  attr = member.GetCustomAttribute<TAttribute>(inherit: true);
                  return (attr == null ? default(TValue) : valueSelector(attr));
              }
          }
      }
      

      用法:

      int n = ImportSet.GetMaxLength(x => x.Segment);
      

      【讨论】:

        猜你喜欢
        • 2020-12-13
        • 1970-01-01
        • 2015-09-28
        • 2021-11-21
        • 2010-10-29
        • 1970-01-01
        • 1970-01-01
        • 2013-04-16
        • 1970-01-01
        相关资源
        最近更新 更多