Using an extension to populate a combobox from an enum? - c#

Currently I have the following extension to read the Description attribute from an enum:
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
Then in my various forms, I have the following code to populate a combobox from an enum:
var list = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
cbo.DataSource = list;
cbo.DisplayMember = "Value";
cbo.ValueMember = "Key";
The above code works fine. However I would like to move the code in a second extension method similar to this:
public static Dictionary<T, string> FromEnum<T>(this ComboBox cbo) where T: struct, IConvertible
{
var list = Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
cbo.DataSource = list;
cbo.DisplayMember = "Value";
cbo.ValueMember = "Key";
}
But the above code doesn't compile. I get the following errors:
Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type
and
The type or namespace name 'DescriptionAttribute' could not be found (are you missing a using directive or an assembly reference?)
Is it possible have this second extension method working properly?

You can use below code:
public enum SampleEnum
{
[Description("Some Value")]
First = 1,
[Description("Some Value")]
Second = 2,
[Description("Some Value")]
Third = 3,
}
public static IList<ListItem> ToSelectList<T>(this Enum #enum, string firstOption = null)
{
var items = Enum.GetValues(typeof(T)).Cast<T>().ToList<T>();
var list = items.Select(x => new ListItem() { Text = Enum.Parse(typeof(T), x.ToString()).GetDescription(), Value = x.ToString() }).ToList();
if (firstOption != null)
{
list.Insert(0, new ListItem() { Text = firstOption, Value = "" });
}
return list;
}
And use like this:
SampleEnum.First.ToSelectList();
UPDATING
Sorry I forgot to put GetDescrption extension:
public static string GetDescription(this object obj)
{
var fi = obj.GetType().GetField(obj.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
return obj.ToString();
}

Related

Order by enum description

I am working on an ASP.NET MVC projet using EF code first, and I am facing a situation where I need to order by an enum description:
public partial class Item
{
public enum MyEnumE
{
[Description("description of enum1")]
Enum1,
[Description("description of enum2")]
Enum2,
...
}
public MyEnumE MyEnum { get; set; }
}
Here is the Search and SortAndPaginate functions:
public async Task<IPagedList<Item>> Search(ItemCriteria criteria, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
var itemFilter = GenerateFilter(criteria);
var items = entities.Items.Where(itemFilter);
return await SortAndPaginate(items, sortName, sortOrder, pageNb);
}
private async Task<IPagedList<Item>> SortAndPaginate(IQueryable<Item> items, SortableTypeE sortName, SortOrder.TypeE sortOrder, int pageNb)
{
IOrderedQueryable<Item> result = null;
switch (sortName)
{
...
case SortableTypeE.Type:
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(i => i.MyEnum.GetDescription())
: items.OrderByDescending(i => i.MyEnum.GetDescription());
result = result.ThenBy(i => i.SomeOtherProperty);
break;
...
}
if (result != null)
{
return await result.ToPagedListAsync(pageNb, 10);
}
return PagedListHelper.Empty<Item>();
}
The problem is that the Item table can be quite huge.
I thought about calling ToListAsync right after entities.Items.Where(itemFilter) but this will get back all filtered items although I only need one page. Does not sound like a good idea.
But if I don't do that EF won't know about GetDescription() mathod and I can only think about two solutions:
- Change my database column to a string (the enum description) instead of the enum itself (but sounds like a hack to me)
- Or alphabetically order MyEnumE components directly in the enum declaration (seems dirty and quite unmaintainable too)
I'm quite stuck since I'm concerned about performances if I call ToListAsync right after filtering, all other solutions seem dirty, and I absolutely need a IPagedList returned from the Search method.
Would anyone have an idea about how to deal with this issue ?
Thanks a lot.
UPDATE
Here is the GetDescription method (can change it if necessary):
public static string GetDescription(this Enum e)
{
FieldInfo fi = e.GetType().GetField(e.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
else
return e.ToString();
}
SOLUTIONS
I'll finally go for Ivan Stoev's suggestion because my project is mainly based on Linq (using Linq instead of stored procedures etc.), so this solution seems more suitable to my particular case than creating reference tables.
However Niyoko Yuliawan's and Michael Freidgeim's are also really good answers to me, anyone reading this post and having a more database approach should go for their solutions ;)
Thanks a lot to all of you.
I would go with dynamic expression. It's more flexible and can easily be changed w/o affecting the database tables and queries.
However, instead of sorting by description strings in the database, I would create ordered map in memory, associating int "order" value with each enum value like this:
public static class EnumHelper
{
public static Expression<Func<TSource, int>> DescriptionOrder<TSource, TEnum>(this Expression<Func<TSource, TEnum>> source)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var body = ((TEnum[])Enum.GetValues(enumType))
.OrderBy(value => value.GetDescription())
.Select((value, ordinal) => new { value, ordinal })
.Reverse()
.Aggregate((Expression)null, (next, item) => next == null ? (Expression)
Expression.Constant(item.ordinal) :
Expression.Condition(
Expression.Equal(source.Body, Expression.Constant(item.value)),
Expression.Constant(item.ordinal),
next));
return Expression.Lambda<Func<TSource, int>>(body, source.Parameters[0]);
}
public static string GetDescription<TEnum>(this TEnum value)
where TEnum : struct
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum) throw new InvalidOperationException();
var name = Enum.GetName(enumType, value);
var field = typeof(TEnum).GetField(name, BindingFlags.Static | BindingFlags.Public);
return field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? name;
}
}
The usage would be like this:
case SortableTypeE.Type:
var order = EnumHelper.DescriptionOrder((Item x) => x.MyEnum);
result = sortOrder == SortOrder.TypeE.ASC
? items.OrderBy(order)
: items.OrderByDescending(order);
result = result.ThenBy(i => i.SomeOtherProperty);
break;
which would generate expression like this:
x => x.MyEnum == Enum[0] ? 0 :
x.MyEnum == Enum[1] ? 1 :
...
x.MyEnum == Enum[N-2] ? N - 2 :
N - 1;
where 0,1,..N-2 is the corresponding index in the value list sorted by description.
Alternative 1
You can do it by projecting enum into custom value and sort by it.
Example:
items
.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
Entity framework then will generate SQL something like this
SELECT
*
FROM
YourTable
ORDER BY
CASE WHEN Enum = 1 THEN 'Desc One'
WHEN Enum = 2 THEN 'Desc Two'
...and so on
END
If you have a lot of query like this, you can create extension method
public static IQueryable<Entity> OrderByDesc(this IQueryable<Entity> source)
{
return source.Select(x=> new
{
x,
Desc = (
x.Enum == Enum.One ? "Desc One"
: x.Enum == Enum.Two ? "Desc Two"
... and so on)
})
.OrderBy(x=>x.Desc)
.Select(x=>x.x);
}
And call it when you need it
var orderedItems = items.OrderByDesc();
Alternative 2
Another alternative solution is to create additional table that map enum value to enum description and join your table to this table. This solution will be more performant because you can create index on enum description column.
Alternative 3
If you want dynamic expression based on your enum description attribute, you can build yourself
Helper Class
public class Helper
{
public MyEntity Entity { get; set; }
public string Description { get; set; }
}
Get dynamically built expression
public static string GetDesc(MyEnum e)
{
var type = typeof(MyEnum);
var memInfo = type.GetMember(e.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute),
false);
return ((DescriptionAttribute)attributes[0]).Description;
}
private static Expression<Func<MyEntity, Helper>> GetExpr()
{
var descMap = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(value => value, GetDesc);
var paramExpr = Expression.Parameter(typeof(MyEntity), "x");
var expr = (Expression) Expression.Constant(string.Empty);
foreach (var desc in descMap)
{
// Change string "Enum" below with your enum property name in entity
var prop = Expression.Property(paramExpr, typeof(MyEntity).GetProperty("Enum"));
expr = Expression.Condition(Expression.Equal(prop, Expression.Constant(desc.Key)),
Expression.Constant(desc.Value), expr);
}
var newExpr = Expression.New(typeof(Helper));
var bindings = new MemberBinding[]
{
Expression.Bind(typeof(Helper).GetProperty("Entity"), paramExpr),
Expression.Bind(typeof(Helper).GetProperty("Description"), expr)
};
var body = Expression.MemberInit(newExpr, bindings);
return (Expression<Func<MyEntity, Helper>>) Expression.Lambda(body, paramExpr);
}
Call it like this
var e = GetExpr();
items.Select(e)
.OrderBy(x => x.Description)
.Select(x => x.Entity);
Change my database column to a string (the enum description) instead
of the enum itself (but sounds like a hack to me).
Opposite, for data-driven application it's better to describe Item property in the database reference table MyItemProperty(MyPropKey,MyPropDescription) and have MyPropKey column in your Items table.
It has a few benefits, e.g.
allow to add new property values without need to change code;
allow to write SQL reports having all information in the database without writing c#;
performance optimisation can be done on SQL
level just by requesting one page;
no enum - less code to maintain.
To keep it simple and with a good performance, I would order the enum manually, you only have to do it once, and it will help a lot
public enum MyEnumE
{
Enum1 = 3,
Enum2 = 1,
Enum3 = 2, // set the order here...
}
Here's a simplified example using a join:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication
{
public partial class Item
{
public enum MyEnumE
{
[Description("description of enum1")]
Enum1,
[Description("description of enum2")]
Enum2
}
public Item(MyEnumE myEnum)
{
MyEnum = myEnum;
}
public MyEnumE MyEnum { get; set; }
}
class Program
{
private static IEnumerable<KeyValuePair<int, int>> GetEnumRanks(Type enumType)
{
var values = Enum.GetValues(enumType);
var results = new List<KeyValuePair<int, string>>(values.Length);
foreach (int value in values)
{
FieldInfo fieldInfo = enumType.GetField(Enum.GetName(enumType, value));
var attribute = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
results.Add(new KeyValuePair<int, string>(value, attribute.Description));
}
return results.OrderBy(x => x.Value).Select((x, i) => new KeyValuePair<int, int>(x.Key, i));
}
static void Main(string[] args)
{
var itemsList = new List<Item>();
itemsList.Add(new Item(Item.MyEnumE.Enum1));
itemsList.Add(new Item(Item.MyEnumE.Enum2));
itemsList.Add(new Item(Item.MyEnumE.Enum2));
itemsList.Add(new Item(Item.MyEnumE.Enum1));
IQueryable<Item> items = itemsList.AsQueryable();
var descriptions = GetEnumRanks(typeof(Item.MyEnumE));
//foreach (var i in descriptions)
// Console.WriteLine(i.Value);
var results = items.Join(descriptions, a => (int)a.MyEnum, b => b.Key, (x, y) => new { Item = x, Rank = y.Value }).OrderBy(x => x.Rank).Select(x => x.Item);
foreach (var i in results)
Console.WriteLine(i.MyEnum.ToString());
Console.WriteLine("\nPress any key...");
Console.ReadKey();
}
}
}
I had a similar problem to solve, only that my ordering had to be dynamic, that is the sort by column parameter is a string.
The boolean sorting also had to be customized in that sense that true comes before false (e.g. 'Active' is before 'Inactive').
I'm sharing here the complete code with you, so you can spare your time. In case you find spots for improvement, please feel free to share in a comment.
private static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> query, SortField sortField)
{
var queryParameterExpression = Expression.Parameter(typeof(T), "x");
var orderByPropertyExpression = GetPropertyExpression(sortField.FieldName, queryParameterExpression);
Type orderByPropertyType = orderByPropertyExpression.Type;
LambdaExpression lambdaExpression = Expression.Lambda(orderByPropertyExpression, queryParameterExpression);
if (orderByPropertyType.IsEnum)
{
orderByPropertyType = typeof(int);
lambdaExpression = GetExpressionForEnumOrdering<T>(lambdaExpression);
}
else if (orderByPropertyType == typeof(bool))
{
orderByPropertyType = typeof(string);
lambdaExpression =
GetExpressionForBoolOrdering(orderByPropertyExpression, queryParameterExpression);
}
var orderByExpression = Expression.Call(
typeof(Queryable),
sortField.SortDirection == SortDirection.Asc ? "OrderBy" : "OrderByDescending",
new Type[] { typeof(T), orderByPropertyType },
query.Expression,
Expression.Quote(lambdaExpression));
return query.Provider.CreateQuery<T>(orderByExpression);
}
The shared GetPropertyExpression has been simplified a bit, to exclude the nested property handling.
private static MemberExpression GetPropertyExpression(string propertyName, ParameterExpression queryParameterExpression)
{
MemberExpression result = Expression.Property(queryParameterExpression, propertyName);
return result;
}
Here is the slightly modified code (from the accepted solution) to handle the Enum ordering.
private static Expression<Func<TSource, int>> GetExpressionForEnumOrdering<TSource>(LambdaExpression source)
{
var enumType = source.Body.Type;
if (!enumType.IsEnum)
throw new InvalidOperationException();
var body = ((int[])Enum.GetValues(enumType))
.OrderBy(value => GetEnumDescription(value, enumType))
.Select((value, ordinal) => new { value, ordinal })
.Reverse()
.Aggregate((Expression)null, (next, item) => next == null ? (Expression)
Expression.Constant(item.ordinal) :
Expression.Condition(
Expression.Equal(source.Body, Expression.Convert(Expression.Constant(item.value), enumType)),
Expression.Constant(item.ordinal),
next));
return Expression.Lambda<Func<TSource, int>>(body, source.Parameters[0]);
}
And the boolean ordering as well.
private static LambdaExpression GetExpressionForBoolOrdering(MemberExpression orderByPropertyExpression, ParameterExpression queryParameterExpression)
{
var firstWhenActiveExpression = Expression.Condition(orderByPropertyExpression,
Expression.Constant("A"),
Expression.Constant("Z"));
return Expression.Lambda(firstWhenActiveExpression, new[] { queryParameterExpression });
}
Also the GetEnumDescription has been modified to receive the Type as the parameter, so it can be called without a generic.
private static string GetEnumDescription(int value, Type enumType)
{
if (!enumType.IsEnum)
throw new InvalidOperationException();
var name = Enum.GetName(enumType, value);
var field = enumType.GetField(name, BindingFlags.Static | BindingFlags.Public);
return field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? name;
}
The SortField is a simple abstraction containing the string column property to be sorted upon and the direction of the sort. For the sake of simplicity I am also not sharing that one here.
Cheers!

Enum to list as an extension?

I have various enums that I use as sources for dropdown lists, In order to provide for a user-friendly description, I added a Description attribute to each enum, and then do the following:
var list = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
The above is repetitive because I have to use it in a lot of places. I tried to add an extension method:
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
public static KeyValuePair<T, string> ToList<T>(this Enum source)
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
}
However, I get an exception:
Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type
What is the correct way to use it as an extension (using the above 2 methods)?
What is the correct way to use it as an extension (using the above 2 methods)?
There is no correct way to use it as an extension. Extension methods (similar to instance methods) are used when you have a value (instance) and for instance want to get some information related to that value. So the extension method would make sense if you want to get the description of a single enum value.
However, in your case the information you need (the list of enum value/description pairs) is not tied to a specific enum value, but to the enum type. Which means you just need a plain static generic method similar to Enum.TryParse<TEnum>. Ideally you would constrain the generic argument to allow only enum, but this type of constraint is not supported (yet), so we'll use (similar to the above system method) just where TEnum : struct and will add runtime check.
So here is a sample implementation:
public static class EnumInfo
{
public static List<KeyValuePair<TEnum, string>> GetList<TEnum>()
where TEnum : struct
{
if (!typeof(TEnum).IsEnum) throw new InvalidOperationException();
return ((TEnum[])Enum.GetValues(typeof(TEnum)))
.ToDictionary(k => k, v => ((Enum)(object)v).GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
}
}
and usage:
public enum MyEnum
{
[Description("Foo")]
A,
[Description("Bar")]
B,
[Description("Baz")]
C,
}
var list = EnumInfo.GetList<MyEnum>();
I have this extension method in my stack and use it for the same thing all the time.
public static string Description(this Enum #enum)
{
try
{
var #string = #enum.ToString();
var attribute =
#enum.GetType()
.GetField(#string)
.GetCustomAttribute<DescriptionAttribute>(false);
return attribute != null ? attribute.Description : #string;
}
catch // Log nothing, just return an empty string
{
return string.Empty;
}
}
Example usage:
MyEnum.Value.Description(); // The value from within the description attr.
Additionally, you can use this one to get a IDictionary for binding purposes.
public static IDictionary<string, string> ToDictionary(this Type type)
{
if (!type.IsEnum)
{
throw new InvalidCastException("'enumValue' is not an Enumeration!");
}
var names = Enum.GetNames(type);
var values = Enum.GetValues(type);
return Enumerable.Range(0, names.Length)
.Select(index => new
{
Key = names[index],
Value = ((Enum)values.GetValue(index)).Description()
})
.ToDictionary(k => k.Key, k => k.Value);
}
Use it like so:
var dictionary = typeof(MyEnum).ToDictionary();
Update
Here is a working .NET Fiddle.
public static Dictionary<TEnum, string> ToDictionary<TEnum>(this Type type)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
return Enum.GetValues(type)
.OfType<TEnum>()
.ToDictionary(value => value, value => value.Description());
}
Then use it like this:
public enum Test
{
[Description("A test enum value for 'Foo'")]
Foo,
[Description("A test enum value for 'Bar'")]
Bar
}
typeof(Test).ToDictionary<Test>()
You can create a generic method which would take Enum and Attribute as generic argument.
For getting any attribute, you can create an extension method like:
public static string AttributeValue<TEnum,TAttribute>(this TEnum value,Func<TAttribute,string> func) where T : Attribute
{
FieldInfo field = value.GetType().GetField(value.ToString());
T attribute = Attribute.GetCustomAttribute(field, typeof(T)) as T;
return attribute == null ? value.ToString() : func(attribute);
}
and here is the method for converting it to dictionary:
public static Dictionary<TEnum,string> ToDictionary<TEnum,TAttribute>(this TEnum obj,Func<TAttribute,string> func)
where TEnum : struct, IComparable, IFormattable, IConvertible
where TAttribute : Attribute
{
return (Enum.GetValues(typeof(TEnum)).OfType<TEnum>()
.Select(x =>
new
{
Value = x,
Description = x.AttributeValue<TEnum,TAttribute>(func)
}).ToDictionary(x=>x.Value,x=>x.Description));
}
You can call it this way:
var test = eUserRole.SuperAdmin
.ToDictionary<eUserRole,EnumDisplayNameAttribute>(attr=>attr.DisplayName);
I have used this Enum and Attribute as example:
public class EnumDisplayNameAttribute : Attribute
{
private string _displayName;
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
}
public enum eUserRole : int
{
[EnumDisplayName(DisplayName = "Super Admin")]
SuperAdmin = 0,
[EnumDisplayName(DisplayName = "Phoenix Admin")]
PhoenixAdmin = 1,
[EnumDisplayName(DisplayName = "Office Admin")]
OfficeAdmin = 2,
[EnumDisplayName(DisplayName = "Report User")]
ReportUser = 3,
[EnumDisplayName(DisplayName = "Billing User")]
BillingUser = 4
}
Output:
Another take on this:
class Program
{
//Example enum
public enum eFancyEnum
{
[Description("Obsolete")]
Yahoo,
[Description("I want food")]
Meow,
[Description("I want attention")]
Woof,
}
static void Main(string[] args)
{
//This is how you use it
Dictionary<eFancyEnum, string> myDictionary = typeof(eFancyEnum).ToDictionary<eFancyEnum>();
}
}
public static class EnumExtension
{
//Helper method to get description
public static string ToDescription<T>(this T en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return en.ToString();
}
//The actual extension method that builds your dictionary
public static Dictionary<T, string> ToDictionary<T>(this Type source) where T : struct, IConvertible
{
if(!source.IsEnum || typeof(T) != source)
{
throw new InvalidEnumArgumentException("BOOM");
}
Dictionary<T, string> retVal = new Dictionary<T,string>();
foreach (var item in Enum.GetValues(typeof(T)).Cast<T>())
{
retVal.Add(item, item.ToDescription());
}
return retVal;
}
}
Whenever I need an enumeration (a static list of known values) that need to have something more than just a mere integer value and a string counterpart, I end up using this Enumeration Utility class that essentially gives me java-like enumeration behavior.
So that would be my first option if I were on op's shoes as it would make it really trivial to achieve what he/she wants.
But, assuming this is not an option for op and she/he need to stick with C# enums, I would use a combination of both ehsan-sajjad and frank-j solutions:
Have an extension method to return the description of a given enum
item, which is pretty much what op had already;
Have a static helper method to return a dictionary of items and their respective descriptions for a given enum type.
Here is how I would implement this:
public static class EnumUtils
{
public static string GetDescription(this Enum enumVal)
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof (DescriptionAttribute), false);
return (attributes.Length > 0) ? ((DescriptionAttribute) attributes[0]).Description : null;
}
public static Dictionary<TEnum, string> GetItemsWithDescrition<TEnum>()
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum)
{
throw new InvalidOperationException("TEnum must be an enum type");
}
return Enum
.GetValues(enumType)
.Cast<TEnum>()
.ToDictionary(enumValue => enumValue, enumValue => GetDescription(enumValue as Enum));
}
}
And here is what the usage would look like:
public class EnumUtilsTests
{
public enum MyEnum
{
[Description("Um")]
One,
[Description("Dois")]
Two,
[Description("Tres")]
Three,
NoDescription
}
public void Should_get_enum_description()
{
MyEnum.One.GetDescription().ShouldBe("Um");
MyEnum.Two.GetDescription().ShouldBe("Dois");
MyEnum.Three.GetDescription().ShouldBe("Tres");
MyEnum.NoDescription.GetDescription().ShouldBe(null);
}
public void Should_get_all_enum_values_with_description()
{
var response = EnumUtils.GetItemsWithDescrition<MyEnum>();
response.ShouldContain(x => x.Key == MyEnum.One && x.Value == "Um");
response.ShouldContain(x => x.Key == MyEnum.Two && x.Value == "Dois");
response.ShouldContain(x => x.Key == MyEnum.Three && x.Value == "Tres");
response.ShouldContain(x => x.Key == MyEnum.NoDescription && x.Value == null);
}
}
Try replacing
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
with
.Select(t => new { k = t, v = t.GetAttributeOfType<DescriptionAttribute>().Description)
.ToDictionary(s => s.k, s => s.v)
In your example, the wrong overload of ToDictionary() is being called.

Reflection to Filter List<T>

I am new to Reflection so please excuse my noob question. How can I create a Method that takes two Parameters, a Generic List and a String and then finds all items in that List where any property value matches the string.
So for example we have an object with 3 properties, I pass a list of this object to the method and a search string and it returns back a list of objects where any of the properties may contain the search string.
I can do like this :
var temp = list.AsQueryable().Where("SomeField == 1").Select("it");
But how can I make this method Generic so I can pass any List of Objects to it ?
Thanks in advance...
If you are using Dynamic Linq, try this
public static IEnumerable<T> Filter<T>(IEnumerable<T> source, string searchStr)
{
var propsToCheck = typeof (T).GetProperties().Where(a => a.PropertyType == typeof(string));
var filter = propsToCheck.Aggregate(string.Empty, (s, p) => (s == string.Empty ? string.Empty : string.Format("{0} OR ", s)) + string.Format("{0} == #0", p.Name));
var filtered = source.AsQueryable().Where(filter, searchStr);
return filtered;
}
Use Type.GetProperties() to get all the properties of an object. Use PropertyInfo.GetValue() to get the value of a given property in a given object. You need to figure out how you want a match your string to a DateTime, to numbers, or to other complex objects. Put it all into a function like bool IsMatch(this object obj, string val). Then you can filter your list like list.Where(x => x.IsMatch("something")).
Here you go mate:
private static void Main(string[] args)
{
var list = new List<object> {new {prop1 = "A", prop2 = "B"},new {prop3 = "B", prop4 = "C"}};
var subList = SearchForStringInProperties(list, "C");
}
private static IEnumerable<object> SearchForStringInProperties(IEnumerable<object> list, string searchString)
{
return from obj in list where FindStringInObjProperties(obj, searchString) select obj;
}
private static bool FindStringInObjProperties(object obj, string searchString)
{
return obj.GetType().GetProperties().Any(property => obj.GetType().GetProperty(property.Name).GetValue(obj).ToString().Equals(searchString));
}
If you just want to match the properties with same type as your argument, this extension method can help,
public static class ListExtensions
{
public static IEnumerable<T> MatchWithAnyProperty<T, TK>(this IEnumerable<T> list, TK value)
{
var argType = typeof (TK);
var properties = typeof(T).GetProperties().Where(x => x.PropertyType.IsAssignableFrom(argType));
return list.Where(item => properties.Any(prop =>
{
var propertyValue = prop.GetValue(item, null);
if (value == null)
return propertyValue == null;
return propertyValue.Equals(value);
}));
}
}
This can be used like,
var items = new[]
{
new
{
Name = "Test",
Age = 20,
Test=25
},
new
{
Name = "Hello",
Age = 10,
Test=15
},
new
{
Name = "T2gdhest",
Age = 14,
Test=20
},
new
{
Name = "hai",
Age = 33,
Test=10
},
new
{
Name = "why not",
Age = 10,
Test=33
},
};
var match= items.MatchWithAnyProperty(10);
foreach (var item in match)
{
Console.WriteLine(item.Name);
}
Console.ReadKey();
And there is the old way ...
public static IList<T> MyMethod<T>(IList<T> myList, string filter)
{
if (myList == null) return null;
if (filter == null) return myList;
var tfilter = filter.GetType();
var properties = typeof(T).GetProperties().Where(x => x.PropertyType.FullName == typeof(string).FullName);
if (!properties.Any()) return null;
var res = new List<T>();
foreach(var el in myList)
{
foreach(var p in properties)
{
if ((string)p.GetValue(el) == filter)
{
res.Add(el);
break;
}
}
}
return res;
}

How to get all descriptions of enum values with reflection?

So I need to get a List<string> from my enum
Here is what I have done so far:
enum definition
[Flags]
public enum ContractorType
{
[Description("Recipient")]
RECIPIENT = 1,
[Description("Deliver")]
DELIVER = 2,
[Description("Recipient / Deliver")]
RECIPIENT_DELIVER = 4
}
HelperClass with method to do what I need:
public static class EnumUtils
{
public static IEnumerable<string> GetDescrptions(Type enumerator)
{
FieldInfo[] fi = enumerator.GetFields();
List<DescriptionAttribute> attributes = new List<DescriptionAttribute>();
foreach (var i in fi)
{
try
{
yield return attributes.Add(((DescriptionAttribute[])i.GetCustomAttributes(
typeof(DescriptionAttribute),
false))[0]);
}
catch { }
}
return new List<string>{"empty"};
}
}
Now in the line where I yield values, I got a NullReferenceException. Did I miss something? The syntax looks all right to me, but maybe I overlooked something?
Edit:
I'm using .net Framework 4.0 here.
This generic static method works fine for getting a list of descriptions for each value of an enum type of T:
public static IEnumerable<string> GetDescriptions<T>()
{
var attributes = typeof(T).GetMembers()
.SelectMany(member => member.GetCustomAttributes(typeof (DescriptionAttribute), true).Cast<DescriptionAttribute>())
.ToList();
return attributes.Select(x => x.Description);
}
I created these extension methods
public static class EnumExtender
{
public static string GetDescription(this Enum enumValue)
{
string output = null;
Type type = enumValue.GetType();
FieldInfo fi = type.GetField(enumValue.ToString());
var attrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
if (attrs.Length > 0) output = attrs[0].Description;
return output;
}
public static IDictionary<T, string> GetEnumValuesWithDescription<T>(this Type type) where T : struct, IConvertible
{
if (!type.IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
return type.GetEnumValues()
.OfType<T>()
.ToDictionary(
key => key,
val => (val as Enum).GetDescription()
);
}
}
Usage
var stuff = typeof(TestEnum).GetEnumValuesWithDescription<TestEnum>();
Will return a Dictionary<TestEnum, string> with value as keys and descriptions as values. If you want just a list, you can change .ToDictionary to
.Select(o => (o as Enum).GetDescription())
.ToList()
Here is a small reusable solution. This is an abstract class which will extract all the attributes of type K from type T.
abstract class AbstractAttributes<T, K>
{
protected List<K> Attributes = new List<K>();
public AbstractAttributes()
{
foreach (var member in typeof(T).GetMembers())
{
foreach (K attribute in member.GetCustomAttributes(typeof(K), true))
Attributes.Add(attribute);
}
}
}
Should we now want to extract only attributes of DescriptionAttribute type, we would use the following class.
class DescriptionAttributes<T> : AbstractAttributes<T, DescriptionAttribute>
{
public List<string> Descriptions { get; set; }
public DescriptionAttributes()
{
Descriptions = Attributes.Select(x => x.Description).ToList();
}
}
This class will extract only attributes of DescriptionAttribute type from the type T. But to actually use this class in you context you will simply need to do the following.
new DescriptionAttributes<ContractorType>().Descriptions.ForEach(x => Console.WriteLine(x));
This line of code will write out all the descriptions you used as parameters in your attributes of type DescriptionAttribute. Should you need to extract some other attributes, just create a new class that derives from the AbstractAttributes<T, K> class and close its type K with the appropriate attribute.
You need to find the DescriptionAttribute on each field, if it exists and then retrieve the Description attribute e.g.
return enumType.GetFields()
.Select(f => (DescriptionAttribute)f.GetCustomAttribute(typeof(DescriptionAttribute)))
.Where(a => a != null)
.Select(a => a.Description)
If you could have multiple descriptions on a field, you could do something like:
FieldInfo[] fields = enumType.GetFields();
foreach(FieldInfo field in fields)
{
var descriptionAttributes = field.GetCustomAttributes(false).OfType<DescriptionAttribute>();
foreach(var descAttr in descriptionAttributes)
{
yield return descAttr.Description;
}
}
which is more similar to your existing approach.
It think this can solve your problem. If it is not implemented you can return null or an exception. It depends what you need.
public DescriptionAttribute GetDescription(ContractorType contractorType)
{
MemberInfo memberInfo = typeof(ContractorType).GetMember(contractorType.ToString())
.FirstOrDefault();
if (memberInfo != null)
{
DescriptionAttribute attribute = (DescriptionAttribute)
memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault();
return attribute;
}
//return null;
//or
throw new NotImplementedException("There is no description for this enum");
}
So you will use it like this :
DescriptionAttribute attribute = GetDescription(ContractorType.RECIPIENT);
Sorry that I didn't read your question. Here is some code that you can use to take all of the description strings:
public IEnumerable<string> GetAllDescriptionInText()
{
List<string> descList = new List<string>();
foreach (DescriptionAttribute desc in Enum.GetValues(typeof(DescriptionAttribute)))
{
descList.Add(GetDescription(desc).Value);
}
return descList;
}
You can try this
public string ContractorTypeDescription(Enum ContractorType)
{
FieldInfo fi = ContractorType.GetType().GetField(ContractorType.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return ContractorType.ToString();
}
}
This is Dictionary not List
But is is something I use
using System.ComponentModel;
using System.Reflection;
using MyExtensions;
namespace MyExtensions
{
public static class Extension
{
public static string GetDescriptionName(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name == null)
return null;
else
{
FieldInfo field = type.GetField(name);
if (field == null)
return name;
else
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr == null)
return name;
else
return attr.Description;
}
}
}
}
}
namespace EnumDescription
{
class Program
{
public enum enumDateCond : byte
{
[Description("Empty")]
Null = 0,
[Description("Not Empty")]
NotNull = 1,
EQ = 2,
LT = 3,
LE = 4,
GE = 14,
GT = 15
};
static void Main(string[] args)
{
enumDateCond x = enumDateCond.Null;
string description = x.GetDescriptionName();
foreach (enumDateCond enm in Enum.GetValues(typeof(enumDateCond)))
{
description = enm.GetDescriptionName();
Console.WriteLine(description);
}
Console.WriteLine("Dictionary");
Dictionary<enumDateCond, string> DLenumDateCond = EnumToDictionary<enumDateCond>();
foreach(enumDateCond key in DLenumDateCond.Keys)
{
Console.WriteLine(key.ToString() + " " + DLenumDateCond[key]);
}
}
public static Dictionary<T, string> EnumToDictionary<T>()
where T : struct
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Dictionary<T, string> enumDL = new Dictionary<T, string>();
foreach (T enm in Enum.GetValues(enumType))
{
string name = Enum.GetName(enumType, enm);
if (name != null)
{
FieldInfo field = enumType.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
name = attr.Description;
}
}
enumDL.Add(enm, name);
}
return enumDL;
}
}
}

enum description value to dropdownlist

I am new to C# and I have a question,
I have a a enum something like
public enum
{
[Description("1,2,3")]
123,
[Description("3,4,5")]
345,
[Description("6,7,8 ")]
678,
}
Now I want the enum descriptions to bind to a dropdownlist.. can some one help me..
thanks in advance!
PS: I am sorry if I am not clear..Let me know if I need to be more specific
public static class EnumExtensionMethods
{
public static string GetDescription(this Enum enumValue)
{
object[] attr = enumValue.GetType().GetField(enumValue.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false);
return attr.Length > 0
? ((DescriptionAttribute) attr[0]).Description
: enumValue.ToString();
}
public static T ParseEnum<T>(this string stringVal)
{
return (T) Enum.Parse(typeof (T), stringVal);
}
}
//Usage with an ASP.NET DropDownList
foreach(MyEnum value in Enum.GetValues<MyEnum>())
myDDL.Items.Add(New ListItem(value.GetDescription(), value.ToString())
...
var selectedEnumValue = myDDL.SelectedItem.Value.ParseEnum<MyEnum>()
//Usage with a WinForms ComboBox
foreach(MyEnum value in Enum.GetValues<MyEnum>())
myComboBox.Items.Add(new KeyValuePair<string, MyEnum>(value.GetDescription(), value));
myComboBox.DisplayMember = "Key";
myComboBox.ValueMember = "Value";
...
var selectedEnumValue = myComboBox.SelectedItem.Value;
These two extension methods have been invaluable to me for going on 5 years and two different jobs, for exactly your stated need.
This is how you would write it:
public enum Test
{
[Description("1,2,3")]
a = 123,
[Description("3,4,5")]
b = 345,
[Description("6,7,8")]
c = 678
}
//Get attributes from the enum
var items =
typeof(Test).GetEnumNames()
.Select (x => typeof(Test).GetMember(x)[0].GetCustomAttributes(
typeof(DescriptionAttribute), false))
.SelectMany(x =>
x.Select (y => new ListItem(((DescriptionAttribute)y).Description)))
//Add items to ddl
foreach(var item in items)
ddl.Items.Add(item);
You could build a wrapper class that looks for the DescriptionAttribute on each member and displays that. Then bind to the wrapper instance. Something like this:
Get the Enum<T> value Description

Categories

Resources