如何从代码中检索数据注释?(以编程方式)

71
我正在使用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注解的值,以在消息中显示,例如:所选择的“玩家名称”为Frank。

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

我需要获取注解的另一个例子:

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

我该怎么做?


你想使用反射来实现这个功能。可以在这里找到一个可行的解决方案。 - thmshd
请查看此帖子https://dev59.com/eHRA5IYBdhLWcg3w2xwI,它向您展示了如何使用反射来完成此操作。 - Jethro
7个回答

101

扩展方法:

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;

2
请纠正我,但是如果Player类中存在多个DisplayAttribute,则它将无法工作(这几乎总是情况)。请参见我在问题中更新的代码。 - user356178
1
如果属性上不存在注释,这将会出错。如果可能不存在注释,请使用FirstOrDefault()而不是First() - devlin carnate
1
样例代码无论如何都会“炸”,因为没有进行空值检查 ;) - jgauffin
1
将属性名称硬编码为字符串会削弱您重构代码的能力。有一天这些代码可能会咬你。 - Dinh Tran
@DinhTran,有没有更好的方法来代替使用字符串作为名称? - Lube
@DinhTran 现在不再是硬编码了。使用真实的属性名称。 - jgauffin

12

试试这个:

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

1
这个可以工作,但如果你不是在实例上操作,而是整个类,你需要将myPlayer.GetType()更改为typeof(Player) - TK-421

10

以下是一些静态方法,您可以使用它们来获取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);
}

}

Using the static method...

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://dev59.com/X2w15IYBdhLWcg3wZq5r#32501356

3

这是我类似做法的方式

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

1

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

我只是为了让EF使用数据注释在我的模型程序集中,但您可以自由地进行分叉和更改。

更改下面的方法HasEFDataAnnotaion并享受乐趣!

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


1
因为https://dev59.com/pmw05IYBdhLWcg3wykzn#7027791上的接受答案仍然使用了魔术常量,所以我分享了基于链接答案的代码:
扩展方法:
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;

0

使用 这里 中提供的 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; 
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接