【问题标题】:Combine Multiple Linq Expressions [duplicate]组合多个 Linq 表达式 [重复]
【发布时间】:2018-08-10 23:51:22
【问题描述】:

我正在重构一些代码,试图使其更具自我记录性。当前代码对 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(即现在处于成功或失败状态)、MessageIsInboundResponseNotYetSent(响应日期为空)。 我可以将这些与多个 where 语句结合起来,如下所示:

return context.MessageLog
    .Where(MessageLogExpression.MessageIsProcessed)
    .Where(MessageLogExpression.MessageIsInbound)
    .Where(MessageLogExpression.ResponseNotYetSent);

MessageLogExpression 是我用来包含这些预定义表达式的类)。

问题 1

这是组合语句的最佳方式,还是会冒先过滤错误字段的风险(例如,Linq 是否将所有条件组合到单个查询中并允许查询引擎(以 SQL 术语)确定最佳执行计划;还是我们强制它先过滤状态字段?

问题 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;
        }
    }
}

【问题讨论】:

    标签: c# linq odata linq-expressions


    【解决方案1】:

    关于#1 - 就 EF 和 Linq to Entities 而言,我希望 EF 会创建相同的查询,无论我是否将其拆分为多个 where 条件或将所有内容合二为一。即使它不会 - SQL 数据库可能仍会产生相同的查询执行计划,因为它有自己的优化器。

    关于其他问题,我们正在使用基于这篇博文的帮助类: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&lt;YourEntityHere&gt;().And(... expression1 ...).And(...)... 开始,类似地以 false 开头:PredicateBuilder.False&lt;YourEntityHere&gt;().Or(...)...

    这意味着对于第三季度,您可以:

    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;
        }
    }
    

    【讨论】:

    • 太棒了;谢谢。发布后不久,我遇到了 OOTB AndOr 表达式方法 (msdn.microsoft.com/en-us/library/…),并认为我已经解决了自己的问题;然后意识到它们不是通用的,所以对我不起作用......你的解决方案给了我他们所做的,但以一种有效的方式;谢谢。
    • @JohnLBevan 仅供参考我找到了我们从中获取解决方案的原始博客文章 - albahari.com/nutshell/predicatebuilder.aspx- 也许还有更多关于它的文章
    • 感谢您的链接。仅供参考:关于我关于构建表达式表达式的最后一部分(即,而不仅仅是将它们应用于 IQueryable)我假设我添加到问题末尾的代码是正确的(添加在那里,因为它是多行的,如果正确的话答案的一部分;如果错了,我深表歉意)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多