Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve Data Annotations from code? (programmatically)

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;

try this:

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

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


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

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;

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