【问题标题】:Build an OR query expression progressively [duplicate]逐步构建 OR 查询表达式 [重复]
【发布时间】:2013-03-18 15:13:37
【问题描述】:

在 LINQ 中,可以按如下方式逐步构建 LINQ 查询:

var context = new AdventureWorksDataContext();
// Step 1
var query = context.Customers.Where(d => d.CustomerType == "Individual");
// Step 2
query = query.Where(d => d.TerritoryID == 3);

上述查询将产生一个等效的 SQL 语句,其中包含一个由 AND 逻辑运算符组合在一起的两个谓词组成的 WHERE 子句,如下所示:

SELECT * FROM Customers WHERE CustomerType = 'Individual' AND TerritoryID = 3

是否可以构建一个 LINQ 查询以生成等效的 SQL 语句 progressively,从而使生成的查询具有 WHERE 子句,其中谓词通过 OR 逻辑运算符组合在一起,如下所示?

SELECT * FROM Customers WHERE CustomerType = 'Individual' OR TerritoryID = 3

【问题讨论】:

  • 为什么不Where(d => d.TerritoryID == 3 || CustomerType = 'Individual');
  • @CuongLe:在某些情况下,您需要逐步构建查询。正如我在上面演示的那样,LINQ 为您提供了这种能力,并且使用 AND 逻辑运算符组合了生成的谓词。我正在寻求的是一种做同样的方式,但使用 OR 逻辑运算符组合谓词

标签: c# .net sql linq


【解决方案1】:

您需要先构建过滤器,然后将过滤器组合成一个可用作组合查询的 lambda:

var filters = new List<Expression<Func<YourType, bool>>>();
filters.Add(d => d.TerritoryID == 3);
filters.Add(d => d.CustomerType == "Individual");
...

var lambda = AnyOf(filters.ToArray());
// this is: d => d.TerrotoryID == 3 || d.CustomerType == "Individual";

var data = src.Where(lambda);

使用:

static Expression<Func<T,bool>> AnyOf<T>(
          params Expression<Func<T,bool>>[] expressions)
{
    if (expressions == null || expressions.Length == 0) return x => false;
    if (expressions.Length == 1) return expressions[0];

    var body = expressions[0].Body;
    var param = expressions[0].Parameters.Single();
    for (int i = 1; i < expressions.Length; i++)
    {
        var expr = expressions[i];
        var swappedParam = new SwapVisitor(expr.Parameters.Single(), param)
                            .Visit(expr.Body);
        body = Expression.OrElse(body, swappedParam);
    }
    return Expression.Lambda<Func<T, bool>>(body, param);
}
class SwapVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public SwapVisitor(Expression from, Expression to){
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

【讨论】:

  • 这使我不必引入第三方 LINQKit 库。谢谢。
  • 非常优雅的代码
【解决方案2】:

如果你想分两步,你可以使用联合:


var context = new AdventureWorksDataContext();
// Step 1
var query = context.Customers.Where(d => d.CustomerType == "Individual");
// step2 
query = query.Union(context.Customers.Where(d => d.TerritoryID == 3));

【讨论】:

    【解决方案3】:

    要在不使用动态 linq 库的情况下完成您的要求,您可以在 where 子句中为每个测试定义表达式,然后使用它们来构建 lambda 表达式:

    Expression<Func<Customer, bool>> isIndividualCustomer = c => c.CustomerType == "Individual";
    
    Expression<Func<Customer, bool>> territoryIdIs3 = c => c.TerritoryID == 3;
    
    Expression<Func<Car, bool>> customerIsIndividualOrOfTerritoryId3 = Expression.Lambda<Func<Customer, bool>>(
        Expression.Or(isIndividualCustomer.Body, territoryIdIs3.Body), isIndividualCustomer.Parameters.Single());
    

    用法:

    var query = context.Customers.Where(customerIsIndividualOrOfTerritoryId3);
    

    【讨论】:

    • 重要:“从范围''引用'客户'类型的变量'c',但未定义”(来自运行时;如果不重新映射参数,您将无法做到这一点- 例如通过访客);另外,应该是OrElse,而不是Or。非常轻微,但我认为您的意思是最后一行中的Customer
    【解决方案4】:

    虽然你可以直接做这种事情,但我使用了 LinqKit 库,它使这种渐进式查询构建变得非常容易:

    你最终会得到:

    var context = new AdventureWorksDataContext();
    // Step 1
    var query = context.Customers.Where(d => d.CustomerType == "Individual");
    // Step 2
    query = query.Or(d => d.TerritoryID == 3);
    

    我正在开发的系统需要很多这样的东西,而 LinqKit 是一个重要的救生员。你可以找到它here

    (注意:无论如何,我不隶属于 LinqKit 的开发人员 - 只是一个粉丝。)

    【讨论】:

    • 我认为这种语法无效。您不能在查询中添加“或”,您必须将其添加到表达式中,这是主要的交易破坏者。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-03
    • 2017-03-01
    • 2022-01-05
    • 1970-01-01
    • 2021-01-09
    相关资源
    最近更新 更多