需要帮助在Linq中添加Where子句

时间:2009-12-16 22:56:31

标签: c# linq linq-to-entities

下面是我正在尝试做的粗略简化的代码示例,但它没有按预期工作。如果重要的话,我正在使用Linq to Entity。

基本上在带有DateClause的第一个循环中,如果我有两个日期子句,则返回任何内容,对一个​​日期子句很有效。在TextClause的循环中,它似乎忽略了第一个之后的每个文本子句。

我的期望是这些子句将被AND组合在一起,因此如果我执行一个文本子句然后再做另一个,则应该添加它们,我会得到更集中的结果,特别是使用Contains方法。

对日期的期望是我希望能够用这个做日期范围,但是如果有两个日期它总是不返回任何内容,即使我知道在该范围内有正确日期的记录。 / p>

我确信我做错了什么或者说是愚蠢的,但是我看不到它。

enum Operators
{
    Contains,
    DoesNotContain,
    GreaterThan,
    LessThan
}

public void DoSomething(List<DateClauses> dateClauses, List<TextClauses> textClauses)
{
    var query = from t in context.Table
                where t.Enabled = true
                select new
                {
                    title = t.title
                    date = t.date
                }


    foreach (DateClause clause in dateClauses)
    {
        switch (clause.Operator)
        {
            case Operator.GreaterThan:
                query = query.Where(l => l.date > clause.Date);
                break;
            case Operator.LessThan
                query = query.Where(l => l.date < clause.Date);
                break;
        }       
    }

    foreach (TextClause clause in textClauses)
    {
        switch (clause.Operator)
        {
            case Operator.Contains:
                query = query.Where(l => l.title.Contains(clause.Text));
                break;
            case Operator.DoesNotContain
                query = query.Where(l => !l.title.Contains(clause.Text));
                break;
        }
    }
}

编辑:更新了示例代码,以显示在此过程中使用枚举。当我将enum的使用外推到一些非常好的解决方案(否则将与bool一起使用)时,在Joel的回复中引起我的评论中显示的异常。

我想说我喜欢到目前为止我在回复中看到的内容,并且我学到了一些新的Linq技巧,我为这个例子道歉,因为我认为bool vs enum与Linq无关。我希望这些变化有助于找到一个对我有用的解决方案。再次感谢您迄今为止的出色回应。

3 个答案:

答案 0 :(得分:4)

只是一个疯狂的猜测,但是将你的循环改为以下可能有不同的结果吗?

foreach (DateClause clause in dateClauses)
{
    var capturedClause = clause;
    switch (clause.Operator)
    {
            case Operator.GreaterThan:
                    query = query.Where(l => l.date > capturedClause.Date);
                    break;
            case Operator.LessThan
                    query = query.Where(l => l.date < capturedClause.Date);
                    break;
    }               
}

您正在创建捕获循环变量的Where条件,而不是每次迭代的循环变量的值。因此,最后,每个Where将是相同的,使用循环的最后一次迭代的值,因为查询在最后一次迭代之后执行。通过引入临时变量,您将捕获每次迭代的变量。

另见Eric Lippert的blog about this subject

答案 1 :(得分:2)

 var query = context.Table
         .Where(t => t.Enabled 
             && textClauses.Where(c => c.Contains).All(c => t.title.Contains(c.Text) )
             && textClauses.Where(c => c.DoesNotContain).All(c => !t.title.Contains(c.Text) )
             && dateClauses.Where(c => c.GreaterThan).All(c => t.date > c.Date) )
             && dateClauses.Where(c => c.LesserThan).All(c => t.date < c.Date) )
          ).Select(t =>  new {
               title = t.title
               date = t.date
      });

这里最重要的是,您可以重构每个当前foreach循环以使用.All()。或者说,循环中的每个if条件都可以。如果你真的想要,你仍然可以突破每个地方:

var query = context.Table.Where(t => t.Enabled).Select(t =>  new {
               title = t.title
               date = t.date
      });

query = query.Where(t => textClauses.Where(c => c.Contains).All(c => t.title.Contains(c.Text) );
query = query.Where(t => textClauses.Where(c => c.DoesNotContain).All(c => !t.title.Contains(c.Text) );
query = query.Where(t => dateClauses.Where(c => c.GreaterThan).All(c => t.date > c.Date) );
query = query.Where(t => dateClauses.Where(c => c.LesserThan).All(c => t.date < c.Date) );

答案 2 :(得分:0)

我之前没有遇到任何问题 - 也许你应该尝试将.Select移动到最后(在Wheres完成之后)。此外,我大量使用PredicateBuilder来增加动态查询内容的灵活性 - 现在你得到了“和”你的附加语义,我猜你想要“或”语义为至少其中一些。 PredicateBuilder让你轻松实现(而且它是免费的!)。