Getting DisplayAttribute when itering on all enums of an assembly - c#

I'm trying to iterate through all the enums contained in an assembly, and for each of them, build an object which I will return to a frontend.
I can retrieve the text value of a enum item, as well as its numeric value, but I'm trying to get its DisplayAttribute when there is one. (ie: [Display(Name = "Sales Admin")] instead of SalesAdmin)
Here's what I have so far
//represents an enum, that will be consumed by the frontend
public class DataEnumItem
{
public string EnumName { get; set; }
public Dictionary<string, int> Items { get; set; } = new Dictionary<string, int>();
}
private List<DataEnumItem> ReturnDataEnumItems<T>()
{
List<DataEnumItem> results = new List<DataEnumItem>();
var dataEnums = Assembly.GetAssembly(typeof(T)).GetTypes().Where(x => x.IsEnum && x.IsPublic);
foreach (var e in dataEnums)
{
var values = Enum.GetValues(e);
DataEnumItem item = new DataEnumItem();
item.EnumName = e.Name;
foreach (var value in values)
{
item.Items.Add(value.ToString(), (int)value);
}
results.Add(item);
}
return results;
}
So far, that works, but like I said, I wish I could get the DisplayAttribute on each of the enum's item if there is one.
I have a method to do that, but only when I have a property that is strongly typed. For example:
enum MyEnum {
[Display(Name = "First Choice")]
firstChoice = 0
}
public static string GetDisplay(this Enum enumValue)
{
var displayAttr = GetAttribute<DisplayAttribute>(enumValue);
if (displayAttr != null)
return displayAttr.Name;
else
return "";
}
Then using it:
MyEnum x = MyEnum.firstChoice;
string display = x.GetDisplay();
Is there any to retrieve the display attribute when the enum value I have is not strongly typed (ie: retrieved via Enum.GetValues())

You can cast to System.Enum and call your GetDisplay method:
foreach (var e in dataEnums)
{
DataEnumItem item = new DataEnumItem();
item.EnumName = e.Name;
item.Items = Enum.GetValues(e)
.Cast<Enum>()
.Select(ev => (key: ev.GetDisplay(), value: (int)(object)ev))
.ToDictionary(ev => ev.key, ev => ev.value);
results.Add(item);
}
Or just substitute you dictionary addition with:
item.Items.Add(((Enum)value).GetDisplay(), (int)value)
Or, if previous options for some reason does not work for you:
e.GetField(value.ToString()).GetCustomAttributes<DisplayAttribute>()

Related

Creating a select list from an list of objects that have one enum property [duplicate]

This question already has answers here:
How do you create a dropdownlist from an enum in ASP.NET MVC?
(36 answers)
Closed 5 years ago.
I have a list of objects of a class like:
public class Template
{
public TemplateTypeEnum TemplateType { get; set; }
public int Id { get; set; }
public int Name { get; set; }
/// etc...
}
And enum:
public enum TemplateTypeEnum
{
[StringValue("First item")]
FirstItem = 1,
[StringValue("Second item")]
SecondItem = 2,
// .....
}
and I have a list of Template objects:
IEnumerable<Template> templateList = GetAllTemplates();
and I would like to populate a select list only from Enum values, so that data value field is int representation of the enum value, and data text file is the string value of the enum, so something like:
Model.TemplateList = new SelectList(templateList, "(int)TemplateType", "TemplateType.ToString()");
var templates = new List<Template>();
var values = Enum.GetValues(typeof(TemplateTypeEnum));
foreach (var value in values)
{
templates.Add(new Template() { Name = Enum.GetName(typeof(TemplateTypeEnum), value), TemplateType = (TemplateTypeEnum)value, Id = (int)value });
}
Later edit:
For retrieving attribute name, it may help the answers given on a similar question, here: Getting attributes of Enum's value
"data text file is the string value of the enum"
This is ambiguous so I'm assuming you want the attribute StringValue in the list, rather than the actual representation of the enum value as a string.
Given an extension method like this:
public static class AttributeExtensions {
public static TResult GetEnumAttributeValue<TEnum, TAttribute, TResult>(this TEnum value,
Func<TAttribute, TResult> valueFunc)
where TAttribute : Attribute
where TEnum : struct, IComparable, IFormattable {
var field = typeof(TEnum).GetField(value.ToString());
if (field == null) {
return default(TResult);
}
var attribute = field.GetCustomAttribute<TAttribute>();
if (attribute == null) {
return default(TResult);
}
return valueFunc.Invoke(attribute);
}
}
You can retrieve the attribute values for the list: you have to cheat a bit in order to use the SelectList constructor by creating an interim list with properties you can pass as strings: this example assumes your StringValue attribute has a property called Value
var interimList = templateList.Select(t => new
{
Id = (int)t.TemplateType,
Value = t.TemplateType.GetEnumAttributeValue((StringValue) s) => s.Value);
});
Model.TemplateList = new SelectList(interimList, "Id", "Value");
(It also assumes that you can use an anonymous type to populate a SelectList..)
You can use the built-in EnumDropDownListFor method.
Change the attribute on your enum to use DisplayAttribute:
public enum TemplateTypeEnum
{
[Display(Name = "First item")]
FirstItem = 1,
[Display(Name = "Second item")]
SecondItem = 2
}
View model:
public class Template
{
public TemplateTypeEnum? TemplateType { get; set; }
}
Razor:
#Html.EnumDropDownListFor(x => x.TemplateType)

How do I convert from dynamic object to specified Type?

Having a model of type Dictionary<string,dynamic> and would like to convert it to Dictionary<string, MyType1> or Dictionary<string, MyOtherType>!
I've tried
var converted = (Dictionary<string,MyType1>)model
without success tried
IConvertible iConv = model; var converted = iConv.ToType(typeof(MyOtherType), null);
too but it doesn't work
Exception: Cannot convert system.object to type x
How do I convert from runtime type (dynamic) to a well known Type?
There is no built-in conversion from one dictionary type to another dictionary type. However, using Enumerable.ToDictionary, you can easily create a new dictionary from any other data structure.
In your particular example, you can use it as
var converted = model.ToDictionary(kv => kv.Key, kv => (MyType1) kv.Value);
Of course this will throw an exception if your values aren't actually of type MyType1. If they aren't, then instead of (MyType1) kv.Value, call some custom conversion function at that point.
The following little demo works for simple types:
MapDynamicToDictionary test shows turning the dynamic to a dictionary.
MapDictionaryToType shows converting the dictionary to a type T.
You could improve on this by doing checks for types or using as etc.
public class Test
{
[Fact]
public void MapDynamicToDictionary()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
Assert.Equal(2, dictionary.Keys.Count);
}
[Fact]
public void MapDictionaryToType()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
var instance = new MyType();
Map(dictionary, instance);
Assert.Equal(instance.Nr, 1);
Assert.Equal(instance.Name, "Devon");
}
public static void Map<T>(IDictionary<string, object> dictionary, T instance)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
foreach (var prop in instance.GetType().GetProperties(attr))
{
if (prop.CanWrite)
{
if(dictionary.ContainsKey(prop.Name))
{
var v = Convert.ChangeType(dictionary[prop.Name], prop.PropertyType);
prop.SetValue(instance, v); }
}
}
}
public static IDictionary<string, object> TurnObjectIntoDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var prop in data.GetType().GetProperties(attr))
{
if (prop.CanRead)
{
dict.Add(prop.Name, prop.GetValue(data, null));
}
}
return dict;
}
}
class MyType
{
public int Nr { get; set; }
public string Name { get; set; }
}
Could use TypeConverter to handle more complex examples. Nice example here: http://putridparrot.com/blog/type-conversions-in-c/
I would put a static constructor on your well known type, which accepts dynamic, and build the well known type from that. e.g.
public class SomeType
{
public static SomeType FromDynamic(dynamic arg)
{
return new SomeType
{
SomeProperty = arg.SomeProp
}
}
public int SomeProperty {get; set; }
}
Then you'd just have to iterate over your Dictionary<string,dynamic> and build up the new object like:
var dictionary = new Dictionary<string, SomeType>();
foreach(var item in model)
{
dictionary.Add(item.Key, SomeType.FromDynamic(item.Value));
}
Or borrowing from #hvd:
var converted = model.ToDictionary(kv => kv.Key, kv => SomeType.FromDynamic(kv.Value));

how to yield an anonymous class in IEnumerable.GroupBy<T> "on the fly" (without enumerating result) in C# LINQ?

I do like this now (thanks to StackOverflow):
IEnumerable<object> Get()
{
var groups = _myDatas.GroupBy(
data => new { Type = data.GetType(), Id = data.ClassId, Value = data.Value },
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}));
foreach (var item in groups)
{
yield return item;
}
}
IEnumerable<MyData> _myDatas;
But is possible to make faster or more "elegant" by not having last foreach loop, but yielding it when the group/anonymous class instance is created?
I would guess fastest way would be to write it open and:
sort the _myDatas
enumerate it and when group changes yield the last group
But I'm trying to learn some LINQ (and C# features in general) so I don't want to do that.
The rest of example is here:
public abstract class MyData
{
public int ClassId;
public string Value;
//...
}
public class MyAction : MyData
{
//...
}
public class MyObservation : MyData
{
//...
}
You should be able to return groups directly, though you might need to change your return type from IEnumerable<Object> to just IEnumerable.
So:
IEnumerable Get()
{
var groups = _myDatas.GroupBy(
// Key selector
data => new {
Type = data.GetType(),
Id = data.ClassId,
Value = data.Value
},
// Element projector
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}
);
return groups;
}
groups has the type IEnumerable< IGrouping< TKey = Anonymous1, TElement = Anonymous2 > >, so you can return it directly.

Select new {Unknown fields} from IEnumerable<UnknownType>

I am using VS2010 and EF4.0. The goal is to select fields of any IEnumerable, in order to show in the DataGridView. Take Northwind.Employees as example, the following code is OK.
private void button1_Click(object sender, EventArgs e)
{
NorthwindEntities en = new NorthwindEntities();
dataGridView1.DataSource = SelectNew(en.Employees, new string[] { "EmployeeID", "FirstName" });
}
public object SelectNew(object items, string[] fields)
{
IEnumerable<Employee> ems = items as IEnumerable<Employee>;
return ems.Select(em => new
{
id = em.EmployeeID,
name = em.FirstName
}
).ToArray();
}
The parameter object items is IEnumerable of EntityObject, and the function will be executed at client side memorry and shall have nothing to do with database now.
But I don't know the EntityObject type (Employee) until runtime, so maybe some complex reflection will be used.
I have checked this,
but when I bind the result to the control, it showed only blank rows without any column or data. And the funciton is for IQueryable, I have tried IEnumerable.AsQueryable and pass to it, but the results did not show any column either.
I've modified the example I pointed to in my comment above. This actually returns an IEnumerable<Dictionary<string,object>>, where each Dictionary represents one of the "new objects", and each key value pair in the dictionary represents a property and its value. Perhaps you can modify this for your use?
I'm not sure if you can simply bind the result to the DataGrid, but you should be able to figure it out.
I don't believe it's possible to create an anonymous type on the fly... But it might be possible to change this to use a dynamic type like ExpandoObject instead of a Dictionary. See this question for some hints on how to do that. I've never used dynamic objects, so you're on your own there!
public class TestClassA {
public string SomeString { get; set; }
public int SomeInt { get; set; }
public TestClassB ClassB { get; set; }
}
public class TestClassB {
public string AnotherString { get; set; }
}
public class Program {
private static void Main(string[] args) {
var items = new List<TestClassA>();
for (int i = 0; i < 9; i++) {
items.Add(new TestClassA {
SomeString = string.Format("This is outer string {0}", i),
SomeInt = i,
ClassB = new TestClassB { AnotherString = string.Format("This is inner string {0}", i) }
});
}
var newEnumerable = SelectNew(items, new string[] { "ClassB.AnotherString" });
foreach (var dict in newEnumerable) {
foreach (var key in dict.Keys)
Console.WriteLine("{0}: {1}", key, dict[key]);
}
Console.ReadLine();
}
public static IEnumerable<Dictionary<string, object>> SelectNew<T>(IEnumerable<T> items, string[] fields) {
var newItems = new List<Dictionary<string, object>>();
foreach (var item in items) {
var dict = new Dictionary<string, object>();
foreach (var field in fields)
dict[field] = GetPropertyValue(field, item);
newItems.Add(dict);
}
return newItems;
}
private static object GetPropertyValue(string property, object o) {
if (property == null)
throw new ArgumentNullException("property");
if (o == null)
throw new ArgumentNullException("o");
Type type = o.GetType();
string[] propPath = property.Split('.');
var propInfo = type.GetProperty(propPath[0]);
if (propInfo == null)
throw new Exception(String.Format("Could not find property '{0}' on type {1}.", propPath[0], type.FullName));
object value = propInfo.GetValue(o, null);
if (propPath.Length > 1)
return GetPropertyValue(string.Join(".", propPath, 1, propPath.Length - 1), value);
else
return value;
}
}

Access Enum custom attributes whilst looping over values

I have added a custom attribute to some enum values (to give them a screen friendly string value). I am trying to build a list of SelectListItems for use on an MVC page, but I am running into trouble accessing the custom attribute.
My enum looks like this.
public enum MyEnum
{
[StringValue("None")] None = 0,
[StringValue("First Value")] FirstValue = 1,
[StringValue("Second Value")] SecondValue = 2
}
The attribute looks like this.
public class StringValueAttribute : Attribute
{
public StringValueAttribute(string value)
{
this.StringValue = value;
}
public string StringValue { get; protected set; }
}
I created a helper class so that I can easily access the StringValue attribute from an instance of the Enum.
public static string GetStringValue(this Enum value)
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(value.ToString());
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
return attribs != null && attribs.Length > 0 ? attribs[0].StringValue : null;
}
I could call it like this.
MyEnum test = MyEnum.FirstValue;
string stringValue = test.GetStringValue();
Finally, onto the code I am stuck with. I can loop over the Enum Values easily, but the values are not instances of MyEnum so I cannot call my helper function. And when I try to access the FieldInfo it always returns null. Here is what I have so far.
public static List<SelectListItem> GetFlagsSelectList<T>(int? selectedValue)
{
List<SelectListItem> items = new List<SelectListItem>();
foreach (int value in Enum.GetValues(typeof(T)))
{
items.Add(new SelectListItem
{
Text = Enum.GetName(typeof(T), value),
Value = value.ToString(),
Selected = selectedValue.HasValue && selectedValue.Value == value
});
}
return items;
}
Is it possible to access the custom attribute within the foreach loop?
EDIT:
I think I asked this unclearly. I would like to access the custom attribute inside the foreach loop. Calling Enum.GetName(typeof(T), value) simply returns the name of the property (e.g. FirstValue) which I do NOT want.
I would like to do something like:
foreach (int value in Enum.GetValues(typeof(T)))
{
string name = Enum.ToObject(typeof (T), value).GetStringValue();
}
But T could be any type so I can't call my GetStringValue() method there.
I have tried doing this:
foreach (int value in Enum.GetValues(typeof(T)))
{
FieldInfo fieldInfo = typeof(T).GetField(value.ToString());
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
string name = attribs != null && attribs.Length > 0 ? attribs[0].StringValue : Enum.GetName(typeof(T), value),;
items.Add(new SelectListItem
{
Text = name,
Value = value.ToString(),
Selected = selectedValue.HasValue && selectedValue.Value == value
});
}
But I always get an exception because the FieldInfo object always returns null.
Try
static string GetStringValue2(Enum value) {
....
}
public static List<SelectListItem> GetFlagsSelectList<T>(int? selectedValue) where T : struct {
var items = new List<SelectListItem>();
foreach (T value in Enum.GetValues(typeof(T))) {
var stringValue = GetStringValue2((Enum)(object)value);
items.Add(new SelectListItem {
Text = Enum.GetName(typeof(T), value),
Value = Convert.ToInt32(value).ToString(),
Selected = selectedValue.HasValue && selectedValue.Value == Convert.ToInt32(value)
});
}
return items;
}
I wrote a blog post about this a while back (for the XmlEnumAttribute, but the same applies here).
public static string ConvertToString(Enum e)
{
// Get the Type of the enum
Type t = e.GetType();
// Get the FieldInfo for the member field with the enums name
FieldInfo info = t.GetField(e.ToString("G"));
// Check to see if the XmlEnumAttribute is defined on this field
if (!info.IsDefined(typeof(XmlEnumAttribute), false))
{
// If no XmlEnumAttribute then return the string version of the enum.
return e.ToString("G");
}
// Get the XmlEnumAttribute
object[] o = info.GetCustomAttributes(typeof(XmlEnumAttribute), false);
XmlEnumAttribute att = (XmlEnumAttribute)o[0];
return att.Name;
}
Hope that helps.

Categories

Resources