I am using LINQ to Objects to aggregate:
var summary = from esc in objs
where esc.time.Month == month && esc.time.Year == year
group esc by esc.rlf_id into g
select new {
ID = g.Key,
Total = g.Count(),
Preventable = g.Where(a => a.preventable).Count()
};
My query works as I would expect, but I also want to order the query by arbitrary field(s) in the anonymous type. I found LINQ: Order By Anonymous Type, but it's in VB.NET and requires strongly specifying what field to sort by. I can conceptualize what I want to accompilish with this pseudocode:
query = get all esc in obj
aggregate into anonymous type with key ID
ID = g.Key, Total = g.Count, Preventable = g.Count of preventable
orderby inputField[0], (optional thenby inputField[1], ..., thenby inputField[n])
How do I accomplish:
Sorting by an anonymous type's field in a LINQ query(done - thank you Marko!)- Further sorting by arbitrary field(s) of the anonymous type
I am open to dot syntax or query syntax.
Edit: With Marko Stanojevic's answer, I am able to partially meet my requirements. I was unaware that I am able to chain LINQ methods together like that. I now am able to (and get expected results from):
var summary = from esc in objs
where esc.time.Month == month && esc.time.Year == year
group esc by esc.rlf_id into g
select new {
ID = g.Key,
Total = g.Count(),
Preventable = g.Where(a => a.preventable).Count()
};
summary = summary.OrderBy(e => e.Total);
What I need is something that lets me do: (pseudocode)
summary = summary.<Order/Then>By<(optional)Descending>(e => e.<someUserInput>)
Given some string that specifies how the user wants to sort, obviously I can do:
if (sortQuery.Equals("Total", StringComparison.OrdinalIgnoresCase), bool descending) {
summary = descending ? summary.OrderByDescending(e => e.Total) : summary.OrderBy(e => e.total)
} else if (sortQuery.Equals( /* ... etc */
However, this ends up being ugly quick-like, especially because I would like to use this for a (potentially nearly infinite) growing number of queries. It would also need to account for OrderBy()
versus ThenBy()
.
I wish I was working with C# 4 so I could use dynamic right about now...
The fact that the data type is anonymous is not important and does not change the problem. An anonymous type is a type as another (it just has a special name). Like other types it is completely known at compile time! You can read Anonymous Type vs Dynamic Type to learn more about difference between anonymous and dynamic types.
The difficulty is that the methods (eg
OrderBy
orOrderByDescending
) to invoke and their parameters (eg the keySelectoritem => item.MyFieldName
) are known only at runtime.The solution is to use Reflection.
The code below implements the
OrderByRules
function as an extension method that applies to any collection of typeIQueryable<T>
(So, to any collection of typeIEnumerable<T>
using simply theAsQueryable<T>()
operator.The first rule is processed in a special way to use the
OrderBy
operator and notThenBy
. Then the others are processed recursively.The call to sort operator is performed in the function
OrderByFieldOrPropertyName
. From the field or property reflection information, we construct a lambda expression of the formitem => item.fieldName
. The MakeGenericMethod function is used to construct the concrete method. Basically, it allows you to switch fromOrderBy<T>
toOrderBy<MyData>
.I hope that answers your question.
To be compiled, the code requires that the following namespaces are declared in the header:
Now you can use
OrderByRules
on your context:This will order the collection by Total (descending) and then by Preventable (ascending).
I'm not sure what's the problem. After you calculate summary, you can simply
Properties of the anonymous type are visible right away. If you want to pass it around and have problems, than the easiest solution is to create a class, since you know what it looks like. If you don't want to create custom class for some reason, I guess you can use
Tuple
for example:Then you can sort by
.Item1
or.Item2
etc. Still, I would use custom class since it is more clear what is going on.Using this extensions.
Try this: