69

I'm using System.ComponentModel.DataAnnotations to provide validation for my Entity Framework 4.1 project.

For example:

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

I need to retrieve the Display.Name annotation value to show it in a message such as The chosen "Player Name" is Frank.

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

Another example of why I could need to retrieve annotations:

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

How can I do that?

  • Please take a look at this post http://stackoverflow.com/questions/803221/c-reflection-finding-attributes-on-a-member-field it shows you how you can do this using reflection. – Jethro Aug 11 '11 at 14:33
  • You want to use Reflection to achieve this. A working solution can be found [here](https://stackoverflow.com/questions/5499459/how-to-get-displayattribute-of-a-property-by-reflection/5499578#5499578). – thmshd Aug 11 '11 at 14:32

7 Answers7

101

Extension method:

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

Code:

var name = player.GetAttributeFrom<DisplayAttribute>(nameof(player.PlayerDescription)).Name;
var maxLength = player.GetAttributeFrom<MaxLengthAttribute>(nameof(player.PlayerName)).Length;
mggSoft
  • 992
  • 2
  • 20
  • 35
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 2
    Correct me if I'm wrong, but I think that it wouldn't work if there was more than one DisplayAttribute present in the Player class (which will almost always be the case). See my updated code in my question. –  Aug 11 '11 at 15:15
  • 1
    If the annotation doesn't exist on the attribute, this will bomb. If it's possible that the annotation doesn't exist, use `FirstOrDefault()` instead of `First()` – devlin carnate Jan 05 '17 at 18:55
  • 1
    The sample code will "bomb" anyway, since there is no null check then ;) – jgauffin Jan 05 '17 at 19:25
  • 1
    hard-coding property name as string will remove your ability of refactoring code. One day these code may bite you. – Dinh Tran Dec 10 '18 at 05:28
  • @DinhTran What is correct way to do, instead using name as a string? – Lube Nov 18 '22 at 09:39
  • @DinhTran It's not hard-coded anymore. Using real property names. – jgauffin Nov 20 '22 at 22:36
12

try this:

((DisplayAttribute)
  (myPlayer
    .GetType()
    .GetProperty("PlayerName")
    .GetCustomAttributes(typeof(DisplayAttribute),true)[0])).Name;
cnom
  • 3,071
  • 4
  • 30
  • 60
Byron
  • 129
  • 2
  • 1
    this works, but if you are not operating on an instance, but whole class, you need to change ```myPlayer.GetType()``` to ```typeof(Player)``` – TK-421 Jan 20 '22 at 15:00
10

Here are some static methods you can use to get the MaxLength, or any other attribute.

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

Or using the optional extension method on an instance...

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

Or using the full static method for any other attribute (StringLength for example)...

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

Inspired by the answer here... https://stackoverflow.com/a/32501356/324479

Community
  • 1
  • 1
Carter Medlin
  • 11,857
  • 5
  • 62
  • 68
3

This is how I have done something similar

/// <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;
}
cnom
  • 3,071
  • 4
  • 30
  • 60
1

Because the acceptet answer on https://stackoverflow.com/a/7027791/7173655 still uses magic constants, I share my code based on the linked answer:

Extension method:

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

Usage without magic constants (assuring refactoring does hurt less):

var nameMaxLength = device.GetAttributeFrom<StringLengthAttribute>(nameof(device.name)).MaximumLength;
Adrian Dymorz
  • 875
  • 8
  • 25
1

I think so that this example https://github.com/TeteStorm/DataAnnotationScan can be very usefull.

I maide just to get EF used Data Annotations at my model assembly, but feel free for fork and change as you need.

Change the below method HasEFDataAnnotaion and have fun!

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

0

a Fix for using metadata Class with MetadataTypeAttribute from here

     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; 
    }
Community
  • 1
  • 1
yamsalm
  • 168
  • 10