【问题标题】:Incorporate partial expression in LINQ/SQL query [duplicate]在 LINQ/SQL 查询中合并部分表达式 [重复]
【发布时间】:2021-08-03 21:05:44
【问题描述】:

我有一个在查询中返回 DateTime 列的表达式。

Expression<Func<T, DateTime>> GetDateExpression;

GetDateExpression = td => td.DateShipped;

但现在我想使用该表达式在 SQL 查询中形成过滤器。

query = query.Where(td => GetDateExpression(td) >= startDate);

但这不会编译。

错误 CS1955:不可调用的成员 'DateFilter.GetDateExpression' 不能像方法一样使用。

谁能告诉我如何使用 GetDateExpression 构造上面的 where 子句?

【问题讨论】:

  • @SvyatoslavDanyliv:您是说为此使用第三方库吗?我根本不相信这是必要的。事实上,我很确定它不是。虽然我不知道是否需要手动构建表达式树来完成这项工作。
  • 你必须编写一个方法来构造一个Lambda&lt;Func&lt;T, bool&gt; 表达式,主体为Expression.GreaterThanOrEqual(yourGetDateExpression, valueOfStartDateExpression),你还应该替换yourGetDateExpression 的参数。看看这些答案:stackoverflow.com/questions/38507906/…stackoverflow.com/questions/5430996/…stackoverflow.com/questions/6736505/how-to-combine-two-lambdas
  • @E.Shcherbo:用什么替换yourGetDataExpression?如何合并它包含的表达式?
  • 不是表达式本身,而是它的参数。看东西,你的GetDateExpression中的tdWhere中的td是不同的东西,因此你需要将GetDateExpressiontd替换为Wheretd。据我所知,替换可以通过expression visitor 来完成,您可以通过我发送的链接找到它

标签: c# linq expression expression-trees .net-5


【解决方案1】:

您可以编写一个方法来从两个操作数构造一个新的GreaterThanOrEqual 表达式

这背后的想法如下。您想构建一个如下所示的新表达式:

td => td.DateShipped >= startDate.Value

使用现有表达式 td =&gt; td.DateShipped 的主体,但这里重要的是结果表达式中的 tdGetDateExpression 中的 td 是不同的东西,所以如果你只是编写更大的ThanOrEqual 表达式而不替换你会得到这样的东西:

td1 => td2.DateShipped >= startDate.Value

因此,您需要将td2 替换为td1,以便表达式看起来像我一开始写的那样。因此替换器所做的就是用我们的filterParam 替换它在表达式树中找到的每个ParameterExpression

您可以查看以下答案以了解更多信息:

    Expression<Func<T, bool>> GreaterThanOrEqual(Expression<Func<T, DateTime?>> operand1, Expression<Func<T, DateTime?>> operand2)
    {
        var filterParam = Expression.Parameter(typeof(T));
        var greaterThanOrEqual = Expression.GreaterThanOrEqual(operand1.Body, operand2.Body);
        greaterThanOrEqual = (BinaryExpression) new ParameterReplacer(filterParam).Visit(greaterThanOrEqual);
        
        return Expression.Lambda<Func<T, bool>>(greaterThanOrEqual, filterParam);
    }
                                                                      
    internal class ParameterReplacer : ExpressionVisitor {
        private readonly ParameterExpression _parameter;

        protected override Expression VisitParameter(ParameterExpression node) {
            return base.VisitParameter(_parameter);
        }

        internal ParameterReplacer(ParameterExpression parameter) {
            _parameter = parameter;
        }
    }

然后像这样使用它

    Expression<Func<T, DateTime?>> getDateExpression = m => m.Date;
    Expression<Func<MyClass, bool>> combined = GreaterThanOrEqual(getDateExpression, td => startDate.Value);
    
    query = query.Where(combined);

【讨论】:

  • 谢谢。现在正在努力解决这个问题。请问ParameterReplacer是什么?
  • 我用一些关于此的信息和一些额外的链接更新了我的答案,如果它还不够,需要更多的洞察力。
  • @E.Shcherbo 仅供参考,因为 EF Core 3.0 有 ReplacingExpressionVisitor 可用于执行替换任务。
  • @GuruStron 是的,非常感谢,这对了解很有用
  • @E.Shcherbo 也可以使用 CTRL+L 快捷方式轻松添加链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多