构建表达式树以动态排序 LINQ 中的字典字典

发布于 2024-09-25 09:56:29 字数 2894 浏览 1 评论 0原文

我正在尝试动态构建表达式树,以便可以更改字典字典中包含的数据的排序顺序。有很多关于动态指定排序依据的列的信息,但这并不是我真正遇到问题的部分。我正在与构建表达式树的 MethodCallExpression 作斗争。

出于本示例的目的,我简化了字典:

Dictionary<string, Dictionary<int, int>> data = new Dictionary<string, Dictionary<int, int>>();

我正在尝试构建一个与以下内容等效的表达式:

data.OrderByDescending(someValue)
    .ThenByDescending(someothervalue)
    .ThenByDescending(anothervalue)...etc

其中“ThenBy”或“ThenByDescending”子句的数量在运行时确定。

比方说,需要按键 4、然后 3、然后 1 进行排序的一个示例。我已经确定(我认为)以下表达式可转换为我的 3 个排序顺序:

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]);

因此,在编译时,我可以编写此表达式,并且效果很好:

var sortedResults = dic.OrderByDescending(ex1.Compile()).ThenByDescending(ex2.Compile()).ThenByDescending(ex3.Compile());

但是,由于排序表达式的数量在运行时会有所不同,因此我需要动态构建它,这就是我正在努力的地方。 我知道可以使用 MethodCallExpression 在运行时构建查询表达式。 MSDN 示例显示了这一点:

    // ***** 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 *****

但是,我无法从该示例过渡到使用此示例的词典词典:

Func<KeyValuePair<string, Dictionary<int, int>>, int>

我认为我需要做的是编写如下内容(这是部分伪代码):

    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));
    }

我知道这不是正确的语法,但它应该表明我的想法。有人可以建议如何从 MSDN 示例转移到动态对这本字典的字典进行排序吗?

谢谢
贾森

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 partial 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
Jason

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暗恋未遂 2024-10-02 09:56:29

您可以编写

var result = data.OrderByDescending(someValue)
                 .ThenByDescending(someothervalue)
                 .ThenByDescending(anothervalue); //...etc

var result = data.OrderByDescending(someValue);
result = result.ThenByDescending(someothervalue);
result = result.ThenByDescending(anothervalue);
//...etc

因此,您只需要调用 OrderBy(Descending) 即可获取 IOrderedEnumerable/Queryable,然后可以重复调用 ThenBy(Descending)在它上面。

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]);
    }        
}   

You 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]);
    }        
}   
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文