下面是我正在尝试做的粗略简化的代码示例,但它没有按预期工作。如果重要的话,我正在使用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无关。我希望这些变化有助于找到一个对我有用的解决方案。再次感谢您迄今为止的出色回应。
答案 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让你轻松实现(而且它是免费的!)。