Building expression tree to dynamically sort a dictionary of dictionaries in LINQ
I am attempting to dynamically build up an expression tree so that I can change the sort order for data contained in a dictionary of dictionaries. There is lots of information about dynamically specifying the column to sort by, but this is not really the part I am having problems with. I am struggling with the MethodCallExpression that builds my expression tree.
For purposes of this example, I've simplified the dictionary:
Dictionary<string, Dictionary<int, int>> data = new Dictionary<string, Dictionary<int, int>>();
I'm trying to build up an expression that would be the equivilent of something like this:
data.OrderByDescending(someValue)
.ThenByDescending(someothervalue)
.ThenByDescending(anothervalue)...etc
Where the number of 'ThenBy' or 'ThenByDescending' clauses is determined at runtime.
Lets say one example needed to sort by the keys 4, then 3, then 1. I've established (I think) that the following expressions translate to my 3 sort orders:
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex1 = (r => r.Value[4]);
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex2 = (r => r.Value[3]);
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> ex2 = (r => r.Value[1]);
So at compile time I can write this expression and it works fine:
var sortedResults = dic.OrderByDescending(ex1.Compile()).ThenByDescending(ex2.Compile()).ThenByDescending(ex3.Compile());
However since the number of sort expressions will vary at runtime I need to build this dynamically, which is where I am struggling. I am aware that query expressions can be built up at runtime using MethodCallExpression. The MSDN example shows this:
// ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****
However, I just can't make the transition from this example, to my dictionary of dictionaries that uses this:
Func<KeyValuePair<string, Dictionary<int, int>>, int>
What I think I need to do is write something like this (this is in p开发者_如何学编程artial psuedo code):
private static void Test()
{
var query = data.AsQueryable()
foreach (int key in ListOfRequiredKeys)
{
Expression<Func<KeyValuePair<string, Dictionary<int, int>>, int>> exp = (r => r.Value[key]);
MakeQuery(exp, query);
}
}
private static IQueryable MakeQuery(Expression<Func<KeyValuePair<string, Dictionary<int, int>> exp, IQueryable query)
{
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"ThenBy",
new Type[] { query.ElementType, query.ElementType },
query.Expression,
Expression.Lambda<Expression<Func<KeyValuePair<string, Dictionary<int, int>>>(exp));
}
I know that isn't the correct syntax, but it should indicate my thinking. Can some one advise how to move from the MSDN example, to dynamically sort this dictionary of dictionaries?
Thanks
JasonYou can write
var result = data.OrderByDescending(someValue)
.ThenByDescending(someothervalue)
.ThenByDescending(anothervalue); //...etc
as
var result = data.OrderByDescending(someValue);
result = result.ThenByDescending(someothervalue);
result = result.ThenByDescending(anothervalue);
//...etc
So you just need to call OrderBy(Descending)
to get an IOrderedEnumerable/Queryable
and can then repeated call ThenBy(Descending)
on it.
private static void Test()
{
var query = data.AsQueryable();
var f = ListOfRequiredKeys.First();
var orderedQuery = query.OrderBy(r => r.Value[f]);
foreach (int key in ListOfRequiredKeys.Skip(1))
{
var k = key;
orderedQuery = orderedQuery.ThenBy(r => r.Value[k]);
}
}
精彩评论