【问题标题】:How can I compose an expression tree from the union of several expressions?如何从多个表达式的并集组成表达式树?
【发布时间】:2011-05-26 16:43:46
【问题描述】:

我正在尝试构建一个IQueryable,它将由我的实体模型进行评估。我想向它传递两组 lambda,让它将所有内容组合成一个更复杂的表达式树,然后传递给数据库执行。

这是我目前所拥有的:

public class FilterManager<T>
{
    public List<Expression<Func<T, bool>>> Inclusive { get; set; }
    public List<Expression<Func<T, bool>>> Exclusive { get; set; }

    public IQueryable<T> ApplyFilters(IQueryable<T> query)
    {
        var q = query;

        Exclusive.ForEach(exp => q = q.Where(exp)); //works fine
        Inclusive.ForEach(exp => /* ??? */); 

        return q;
    }

    //ctor, etc.
}

这里的想法是我将几个表达式添加到Inclusive,它们将它们“Ors”在一起。例如,如果Tint,则代码:

fm.Inclusive.Add(x => x > 1);
fm.Inclusive.Add(y => y < 5);

query = fm.ApplyFilters(query);

应该有相同的结果集:

query = query.Where(z => z > 1 || z < 5);

如何在没有第三方工具(例如 PredicateBuilder)的情况下让 Inclusive 工作?第三方工具通常很好,但我想加深对如何在 .NET 中编写表达式的理解。

我还需要确保尚未评估树,以便我可以对数据库进行过滤。这意味着我需要生成 Entity Framework 4.0 可以使用的东西。

【问题讨论】:

  • 复制:stackoverflow.com/questions/5430996/… ? (我的回答显示了 2 种内联表达式的方法;IMO 首选“访问者”版本)
  • @Marc - 谢谢,我现在正在阅读您的链接。伙计,当您深入研究 ExpressionRewriter 时,就会有很多代码。但这适用于OrElse 类型的行为吗?这里的问题是关于AndAlso,我觉得更符合我的Exclusive 设置。
  • 要记住的重要一点是,在您的示例中,每个 x=>x>1 和 x=>x不同的变量。想象一下它们被称为 x1 和 x2。如果你天真地将它们组合成第三个,那么你会得到 x3=>x1>1||x2按引用,而不是按名称。这些名称基本上被忽略了。
  • @Eric - 是的,如果我不清楚,对不起。我的意思是这两个 sn-ps 应该返回相同的结果集,而不必具有相同的内部行为或使用相同的变量。我重命名了变量以使其更清晰。
  • @Marc - 感谢您的链接,我认为您的访问者代码绝对是正确的方法。如果这个简单的版本不能破解它,我将使用你的代码。

标签: c# .net-4.0 entity-framework-4 linq-to-entities expression-trees


【解决方案1】:

我能想到的最接近的匹配是:

public IQueryable<T> ApplyFilters(IQueryable<T> query)
{
    IQueryable<T> q;

    if (!Inclusive.Any())
        q = query;
    else
    {
        q = Enumerable.Empty<T>();
        Inclusive.ForEach(exp => q = q.Union(query.Where(exp)));
    }

    Exclusive.ForEach(exp => q = q.Where(exp));

    return q;
}

但我几乎可以肯定这会非常低效

【讨论】:

  • 不,通过使用访问者来展平表达式可以更干净地完成
  • 我一直在尝试一些类似的事情。不幸的是,这将导致ExclusiveInclusive 之间的错误交互,因为匹配Inclusive 但不匹配Exclusive 的内容将被添加进来。(希望这是有道理的。)
  • @Justin,那么你应该以相反的顺序执行 - 首先添加 Inclusive 过滤器,然后添加 Exclusive。见更新。但是你一定要看看 Marc 的代码
  • 这似乎有效。我所做的唯一更改是将Exclusive.ForEach 调用放在方法的开头。我会用确切的代码添加一个答案,但你的答案就是为我解决了这个问题。谢谢。
【解决方案2】:

试试这样的? 我不确定我没有测试过。

Inclusive.ForEach(exp => q = q.Union(q.Where(exp)));

【讨论】:

  • 试过了。它似乎包含所有内容,因此根本没有过滤最终结果。
【解决方案3】:

即使已经有一个公认的答案,我想指出您可以使用谓词生成器将表达式与Or 结合起来。这会将其保留为对数据库的简单查询。

http://www.albahari.com/nutshell/predicatebuilder.aspx

【讨论】:

    【解决方案4】:

    我还没有在我的实体模型上测试它,所以我不知道它是否会被 EF 支持,但以下适用于 L2O。这只是与 Snowbear JIM-compiler 的代码略有不同:

    public IQueryable<T> ApplyFilters(IQueryable<T> query)
    {
        Exclusive.ForEach(exp => query = query.Where(exp));
    
        if (Inclusive.Count == 0)
        {
            return query;
        }
    
        IQueryable<T> q = Enumerable.Empty<T>().AsQueryable<T>();
        Inclusive.ForEach(exp => q = q.Union(query.Where(exp)));
    
        return q;
    }
    

    【讨论】:

    • 接受了 Snowbear JIM-compiler 的回答,因为这直接导致我找到了这个解决方案。
    猜你喜欢
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-12
    • 1970-01-01
    相关资源
    最近更新 更多