How to retrieve Data Annotations from code? (programmatically)

C#Entity Framework-4.1Data Annotations

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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;

Solution 2 - C#

try this:

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

Solution 3 - C#

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

Solution 4 - C#

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

Solution 5 - C#

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;

Solution 6 - C#

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

Solution 7 - C#

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser356178View Question on Stackoverflow
Solution 1 - C#jgauffinView Answer on Stackoverflow
Solution 2 - C#ByronView Answer on Stackoverflow
Solution 3 - C#Carter MedlinView Answer on Stackoverflow
Solution 4 - C#cnomView Answer on Stackoverflow
Solution 5 - C#Adrian DymorzView Answer on Stackoverflow
Solution 6 - C#Tania StormView Answer on Stackoverflow
Solution 7 - C#yamsalmView Answer on Stackoverflow