合并多个Linq表达式 [英] Combine Multiple Linq Expressions
问题描述
我正在重构一些代码,试图使其更具自我文档编制性.当前代码具有对OData服务的查询,如下所示:
I'm in the process of refactoring some code, attempting to make it more self-documenting. The current code has a query over an OData service which looks like this:
return context.MessageLog.Where
(
x =>
(
x.Status == MessageStatus.Success
|| x.Status == MessageStatus.Failure
)
&& x.Direction == MessageDirection.Inbound
&& x.ResponseDate == new DateTimeOffset(new DateTime(1900, 01, 01))
);
我希望将其更改为使用Linq表达式.
我可以将所有逻辑移动到单个表达式中,并让代码运行context.MessageLog.Where(MessageIsPendingResponse);
.
但是,我想为不同的条件创建表达式:MessageIsProcessed
(即现在处于成功或失败状态),MessageIsInbound
和ResponseNotYetSent
(响应日期为空).
我可以将它们与多个where语句结合起来,像这样:
I'm hoping to change this to make use of Linq Expressions.
I could move all of the logic into a single expression and have code run context.MessageLog.Where(MessageIsPendingResponse);
.
However, I'd like to create expressions for the different conditions: MessageIsProcessed
(i.e. now at success or failure state), MessageIsInbound
and ResponseNotYetSent
(response date is null).
I could combine these with multiple where statements like so:
return context.MessageLog
.Where(MessageLogExpression.MessageIsProcessed)
.Where(MessageLogExpression.MessageIsInbound)
.Where(MessageLogExpression.ResponseNotYetSent);
(MessageLogExpression
是我用来包含这些预定义表达式的类).
(MessageLogExpression
being a class I use to contain these predefined expressions).
这是合并状态的最佳方法,还是首先冒着在错误的字段上进行过滤的风险(例如,Linq是否将所有条件合并到单个查询中,并允许查询引擎(以SQL术语)确定最佳执行计划;还是我们强迫它首先在状态"字段上进行过滤?
Is this the best way to combine the satements, or does it risk filtering on the wrong field first (e.g. does Linq combine all the conditions into a single query and allow the query engine (in SQL terms) to determine the best execution plan; or are we forcing it to filter on the Status field first?
上面的内容非常适合我们有AND
加入表达式的场景;但是我们该怎么做OR
?
我认为有某种方法可以将它们组合在一起,但找不到任何明显的方法.我怀疑这样的东西存在吗?
The above is great for scenarios where we have an AND
joining our expressions; but how would we do an OR
?
I assume there's some way to combine these, but couldn't find anything obvious. I suspect something like this exists?
return context.MessageLog.Where(new OrExpression(MessageIsSuccess,MessageIsFailure));
问题3
是否有一种在另一个表达式定义中组合表达式的好方法;例如类似于以下代码(仅编译版本)?
Question 3
Is there a good way to combine expressions within another expression defintion; e.g. something like the below code (only a version that compiles)?
public static Expression<Func<MessageLogRecord, bool>> MessageIsPendingResponse
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
MessageIsProcessed(x)
&& MessageIsInbound(x)
&& ResponseNotYetSent(x);
return expr;
}
}
附录:上述那些表达式的代码:
Addendum: Code for those expressions described above:
public class MessageLogExpression
{
public static Expression<Func<MessageLogRecord, bool>> MessageIsProcessed
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
(
x.Status == MessageStatus.Success
|| x.Status == MessageStatus.Failure
);
return expr;
}
}
public static Expression<Func<MessageLogRecord, bool>> MessageIsInbound
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
x.Direction == MessageDirection.Inbound;
return expr;
}
}
static readonly DateTimeOffset NullDate = new DateTimeOffset(new DateTime(1900, 01, 01));
public static Expression<Func<MessageLogRecord, bool>> ResponseNotYetSent
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
x.ResponseDate == NullDate; //todo: test if this works or if I have to define the value within the expression
return expr;
}
}
}
推荐答案
关于#1-关于具有Linq to Entities的EF,我希望EF可以创建相同的查询,无论我将其分为多个条件或条件一切都合而为一.即使不会,SQL数据库也可能会产生相同的查询执行计划,因为它具有自己的优化器.
About #1 - in terms of EF with Linq to Entities I'd expect that EF would create the same query regardless if I split it into multiple where conditions or have everything in one. Even if it won't - an SQL database might still produce the same query execution plan, since it has it's own optimizer.
关于其他问题,我们使用的是基于此博客文章的帮助程序类: http://www.albahari.com/nutshell/predicatebuilder.aspx
About the other questions, we are using a helper class which is based on this blog post: http://www.albahari.com/nutshell/predicatebuilder.aspx
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>()
{
return (Expression<Func<T, bool>>) (input => true);
}
public static Expression<Func<T, bool>> False<T>()
{
return (Expression<Func<T, bool>>) (input => false);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
InvocationExpression invocationExpression = Expression.Invoke((Expression) expression2, expression1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>((Expression) Expression.OrElse(expression1.Body, (Expression) invocationExpression), (IEnumerable<ParameterExpression>) expression1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
InvocationExpression invocationExpression = Expression.Invoke((Expression) expression2, expression1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>((Expression) Expression.AndAlso(expression1.Body, (Expression) invocationExpression), (IEnumerable<ParameterExpression>) expression1.Parameters);
}
}
这非常有用,因为它可以帮助您轻松组合表达式.如果要合并OR表达式,则可以从PredicateBuilder.True<YourEntityHere>().And(... expression1 ...).And(...)...
开始,您可以类似地从false开始:PredicateBuilder.False<YourEntityHere>().Or(...)...
This works really nice as it helps you to combine expressions easily. You can start with PredicateBuilder.True<YourEntityHere>().And(... expression1 ...).And(...)...
if you want to merge OR expressions you do it similarly starting with false: PredicateBuilder.False<YourEntityHere>().Or(...)...
这意味着您可以在第三季度执行以下操作:
This means that for Q3 you can do:
public static Expression<Func<MessageLogRecord, bool>> MessageIsPendingResponse
{
get
{
Expression<Func<CCI_Int_ExportLog, bool>> expr = PredicateBuilder.True<MessageLogRecord>()
.And(MessageIsProcessed)
.And(MessageIsInbound)
.And(ResponseNotYetSent)
;
return expr;
}
}
这篇关于合并多个Linq表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!