合并多个 Linq 表达式
Combine Multiple Linq Expressions
我正在重构一些代码,试图使其更加自文档化。当前代码对 OData 服务有一个查询,如下所示:
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 语句结合起来,如下所示:
return context.MessageLog
.Where(MessageLogExpression.MessageIsProcessed)
.Where(MessageLogExpression.MessageIsInbound)
.Where(MessageLogExpression.ResponseNotYetSent);
(MessageLogExpression
是一个 class 我用来包含这些预定义的表达式)。
问题 1
这是组合语句的最佳方式吗,还是它会冒着首先过滤错误字段的风险(例如,Linq 是否将所有条件组合成一个查询并允许查询引擎(在 SQL 术语中) 以确定最佳执行计划;还是我们强制它首先在 Status 字段上进行筛选?
问题 2
以上内容非常适合我们有 AND
加入我们的表达式的场景;但是我们如何做 OR
?
我假设有某种方法可以将它们结合起来,但找不到任何明显的东西。我怀疑存在这样的东西?
return context.MessageLog.Where(new OrExpression(MessageIsSuccess,MessageIsFailure));
问题 3
有没有什么好的方法可以在另一个表达式定义中组合表达式?例如类似于下面的代码(只有一个可以编译的版本)?
public static Expression<Func<MessageLogRecord, bool>> MessageIsPendingResponse
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
MessageIsProcessed(x)
&& MessageIsInbound(x)
&& ResponseNotYetSent(x);
return expr;
}
}
附录:上述表达式的代码:
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 会创建相同的查询,无论我将其拆分为多个 where 条件还是将所有内容都放在一个条件中。即使它不会 - SQL 数据库可能仍会生成相同的查询执行计划,因为它有自己的优化器。
关于其他问题,我们正在使用基于此博客 post 的助手 class: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(...)...
这意味着对于第 3 季度,您可以执行以下操作:
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;
}
}
我正在重构一些代码,试图使其更加自文档化。当前代码对 OData 服务有一个查询,如下所示:
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 语句结合起来,如下所示:
return context.MessageLog
.Where(MessageLogExpression.MessageIsProcessed)
.Where(MessageLogExpression.MessageIsInbound)
.Where(MessageLogExpression.ResponseNotYetSent);
(MessageLogExpression
是一个 class 我用来包含这些预定义的表达式)。
问题 1
这是组合语句的最佳方式吗,还是它会冒着首先过滤错误字段的风险(例如,Linq 是否将所有条件组合成一个查询并允许查询引擎(在 SQL 术语中) 以确定最佳执行计划;还是我们强制它首先在 Status 字段上进行筛选?
问题 2
以上内容非常适合我们有 AND
加入我们的表达式的场景;但是我们如何做 OR
?
我假设有某种方法可以将它们结合起来,但找不到任何明显的东西。我怀疑存在这样的东西?
return context.MessageLog.Where(new OrExpression(MessageIsSuccess,MessageIsFailure));
问题 3
有没有什么好的方法可以在另一个表达式定义中组合表达式?例如类似于下面的代码(只有一个可以编译的版本)?
public static Expression<Func<MessageLogRecord, bool>> MessageIsPendingResponse
{
get
{
Expression<Func<MessageLogRecord, bool>> expr = x =>
MessageIsProcessed(x)
&& MessageIsInbound(x)
&& ResponseNotYetSent(x);
return expr;
}
}
附录:上述表达式的代码:
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 会创建相同的查询,无论我将其拆分为多个 where 条件还是将所有内容都放在一个条件中。即使它不会 - SQL 数据库可能仍会生成相同的查询执行计划,因为它有自己的优化器。
关于其他问题,我们正在使用基于此博客 post 的助手 class: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(...)...
这意味着对于第 3 季度,您可以执行以下操作:
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;
}
}