【问题标题】:How to retrieve Data Annotations from code? (programmatically)如何从代码中检索数据注释? (以编程方式)
【发布时间】:2011-08-11 14:25:30
【问题描述】:

我正在使用 System.ComponentModel.DataAnnotations 为我的 Entity Framework 4.1 项目提供验证。

例如:

public class Player
{
    [Required]
    [MaxLength(30)]
    [Display(Name = "Player Name")]
    public string PlayerName { get; set; }

    [MaxLength(100)]
    [Display(Name = "Player Description")]
    public string PlayerDescription{ get; set; }
}

我需要检索Display.Name 注释值以在消息中显示它,例如选择的“玩家名称”是弗兰克。

================================================ ====================================

我可能需要检索注释的另一个示例:

var playerNameTextBox = new TextBox();
playerNameTextBox.MaxLength = GetAnnotation(myPlayer.PlayerName, MaxLength);

我该怎么做?

【问题讨论】:

标签: c# entity-framework-4.1 data-annotations


【解决方案1】:

扩展方法:

public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
{
    var attrType = typeof(T);
    var property = instance.GetType().GetProperty(propertyName);
    return (T)property .GetCustomAttributes(attrType, false).First();
}

代码:

var name = player.GetAttributeFrom<DisplayAttribute>(nameof(player.PlayerDescription)).Name;
var maxLength = player.GetAttributeFrom<MaxLengthAttribute>(nameof(player.PlayerName)).Length;

【讨论】:

  • 如果我错了,请纠正我,但我认为如果 Player 类中存在多个 DisplayAttribute (几乎总是如此),它将不起作用。在我的问题中查看我更新的代码。
  • 如果该属性上不存在注释,这将爆炸。如果注释可能不存在,请使用FirstOrDefault() 而不是First()
  • 示例代码无论如何都会“轰炸”,因为那时没有空检查;)
  • 将属性名称硬编码为字符串将消除您重构代码的能力。有一天这些代码可能会咬你一口。
【解决方案2】:

试试这个:

((DisplayAttribute)
  (myPlayer
    .GetType()
    .GetProperty("PlayerName")
    .GetCustomAttributes(typeof(DisplayAttribute),true)[0])).Name;

【讨论】:

  • 这个可行,但是如果你不是在一个实例上操作,而是整个类,你需要将myPlayer.GetType()更改为typeof(Player)
【解决方案3】:

这里有一些静态方法可用于获取 MaxLength 或任何其他属性。

using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

public static class AttributeHelpers {

public static Int32 GetMaxLength<T>(Expression<Func<T,string>> propertyExpression) {
    return GetPropertyAttributeValue<T,string,MaxLengthAttribute,Int32>(propertyExpression,attr => attr.Length);
}

//Optional Extension method
public static Int32 GetMaxLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) {
    return GetMaxLength<T>(propertyExpression);
}


//Required generic method to get any property attribute from any class
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute {
    var expression = (MemberExpression)propertyExpression.Body;
    var propertyInfo = (PropertyInfo)expression.Member;
    var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute;

    if (attr==null) {
        throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name);
    }

    return valueSelector(attr);
}

}

使用静态方法...

var length = AttributeHelpers.GetMaxLength<Player>(x => x.PlayerName);

或者在实例上使用可选的扩展方法...

var player = new Player();
var length = player.GetMaxLength(x => x.PlayerName);

或者对任何其他属性使用完整的静态方法(例如 StringLength)...

var length = AttributeHelpers.GetPropertyAttributeValue<Player,string,StringLengthAttribute,Int32>(prop => prop.PlayerName,attr => attr.MaximumLength);

受此处答案的启发... https://stackoverflow.com/a/32501356/324479

【讨论】:

    【解决方案4】:

    这就是我做类似事情的方式

    /// <summary>
    /// Returns the DisplayAttribute of a PropertyInfo (field), if it fails returns null
    /// </summary>
    /// <param name="propertyInfo"></param>
    /// <returns></returns>
    private static string TryGetDisplayName(PropertyInfo propertyInfo)
    {
        string result = null;
        try
        {
            var attrs = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true);
            if (attrs.Any())
                result = ((DisplayAttribute)attrs[0]).Name;
        }
        catch (Exception)
        {
            //eat the exception
        }
        return result;
    }
    

    【讨论】:

      【解决方案5】:

      因为https://stackoverflow.com/a/7027791/7173655 上的接受答案仍然使用魔法常量,所以我根据链接的答案分享我的代码:

      扩展方法:

      public static TA GetAttributeFrom<TC,TA>(string propertyName) where TA : Attribute {
          return (TA)typeof(TC).GetProperty(propertyName)
              .GetCustomAttributes(typeof(TA), false).SingleOrDefault();
      }
      

      没有魔法常数的使用(确保重构确实减少了伤害):

      var nameMaxLength = device.GetAttributeFrom<StringLengthAttribute>(nameof(device.name)).MaximumLength;
      

      【讨论】:

        【解决方案6】:

        我认为这个例子https://github.com/TeteStorm/DataAnnotationScan会很有用。

        我只是为了在我的模型装配中获得 EF 使用的数据注释,但可以根据需要随意进行分叉和更改。

        更改下面的方法 HasEFDataAnotaion 并玩得开心!

        https://github.com/TeteStorm/DataAnnotationScan

        
                private static bool HasEFDataAnnotaion(PropertyInfo[] properties)
                {
                    return properties.ToList().Any((property) =>
                    {
                        var attributes = property.GetCustomAttributes(false);
                        Attribute[] attrs = System.Attribute.GetCustomAttributes(property);
                        return attrs.Any((attr) =>
                        {
                            return attr is KeyAttribute || attr is ForeignKeyAttribute || attr is IndexAttribute || attr is RequiredAttribute || attr is TimestampAttribute
                            || attr is ConcurrencyCheckAttribute || attr is MinLengthAttribute || attr is MinLengthAttribute
                            || attr is MaxLengthAttribute || attr is StringLengthAttribute || attr is TableAttribute || attr is ColumnAttribute
                            || attr is DatabaseGeneratedAttribute || attr is ComplexTypeAttribute;
                        });
                    });
                }
        
        

        【讨论】:

          【解决方案7】:

          使用来自 here 的 MetadataTypeAttribute 的元数据类的修复

               public  T GetAttributeFrom<T>( object instance, string propertyName) where T : Attribute
              {
                  var attrType = typeof(T);
                  var property = instance.GetType().GetProperty(propertyName);
                  T t = (T)property.GetCustomAttributes(attrType, false).FirstOrDefault();
                  if (t == null)
                  {
                      MetadataTypeAttribute[] metaAttr = (MetadataTypeAttribute[])instance.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true);
                      if (metaAttr.Length > 0)
                      {
                          foreach (MetadataTypeAttribute attr in metaAttr)
                          {
                              var subType = attr.MetadataClassType;
                              var pi = subType.GetField(propertyName);
                              if (pi != null)
                              {
                                  t = (T)pi.GetCustomAttributes(attrType, false).FirstOrDefault();
                                  return t;
                              }
          
          
                          }
                      }
          
                  }
                  else
                  {
                      return t;
                  }
                  return null; 
              }
          

          【讨论】:

            猜你喜欢
            • 2013-07-23
            • 2012-08-05
            • 2020-03-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-30
            相关资源
            最近更新 更多