One of the recent questions that was intriguing (for me) in Stackoverflow was the need to Process a List and while displaying the result, display only the properties that are decorated with a specific attribute.
Let me explain with an example. Consider the following Entity.
public class Employee { public int ID { get; set; } public string Place { get; set; } [IncludeinReport] public string BusinessVertical { get; set; } [IncludeinReport] public string Region { get; set; } public string Country { get; set; } [IncludeinReport] public string BusinessUnit { get; set; } }
For a List<Employee>, the expected result would display only “BusinessVertical”, “Region”, and “BusinessUnit” in the result. Let’s begin by creating a mock sample data to test it.
var list = new List<Employee> { new Employee{ID = 1, Region = "Europe", BusinessUnit="Software", BusinessVertical = "Sample1"}, new Employee{ID = 1, Region = "Asia", BusinessUnit="Software", BusinessVertical = "Sample1"}, new Employee{ID = 1, Region = "Asia", BusinessUnit="Hardware", BusinessVertical = "Sample1"}, new Employee{ID = 1, Region = "Europe", BusinessUnit="Software", BusinessVertical = "Sample1"}, new Employee{ID = 1, Region = "Asia", BusinessUnit="Telecom", BusinessVertical = "Sample1"}, new Employee{ID = 1, Region = "Europe", BusinessUnit="Software", BusinessVertical = "Sample1"}, };
Just to make things interesting, we would add restriction that the result needs to be Sorted By Region and Then by BusinessUnit. A LINQ solution would have been ideal, but unfortunately, I couldn’t go all the way with LINQ. But here is the solution I could suggest.
The first step, obviously was to get the List of Properties that has the required Attribute, which we can achieve using Reflection.
var properties = typeof(Employee).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(IncludeinReport))).Select(v=>v); var intermediate = list.OrderBy(c => c.Region).ThenBy(x=>x.BusinessUnit).SelectMany((x,index)=> properties.Select(v=> new {GroupId = index, Dict = new KeyValuePair<string,object>(v.Name, v.GetValue(x))})) ; var resultList = intermediate.GroupBy(x=>x.GroupId).Select(x=>x.ToList().Select(c=>c.Dict).CreateObject());
Where CreateObject is defined as
public static class Extensions { public static ExpandoObject CreateObject(this IEnumerable<KeyValuePair<string,object>> source) { dynamic returnValue = new ExpandoObject(); var dict = returnValue as IDictionary<string, object>; foreach (var kvp in source) { dict.Add(kvp.Key, kvp.Value); } return returnValue; } }
Since even anonymous types needs to have Property Names defined in compile time, the only solution to create a Dynamic Type (including property names) was using ExpandoObject.
This would give you the desired output as enclosed in the screenshot.
Again, this is the solution I could arrive at, there would be better ones. I would certainly be interested to know a better solution. If anyone would like to follow the OP in question, please follow it StackoverFlow