如何生成一个System.Linq.Expressions.Expression对象,该对象包含对Any()的调用

本文关键字:对象 包含 Any 调用 Expressions 何生成 一个 System Linq Expression | 更新日期: 2023-09-27 18:29:04

我想动态生成一个linq.expressions.expression语句,可以将其用作筛选器。

下面是一个示例Linq查询,我想将其转换为表达式:

ctx.customer.where(c=>ctx.Invoice.Any(i=>i.customerId == c.id));

这是我的尝试

using System.Linq.Expressions;
var c = Expression.parameter(typeof(Customer),"c");
var i = Expression.parameter(typeof(Invoice),"i");
var rightPart= Expression.Equal(
 Expression.propertyorField(i,"customerId"), Expression.propertyorfield(c,"id")

请协助。

如何生成一个System.Linq.Expressions.Expression对象,该对象包含对Any()的调用

当我需要手动创建linq表达式时,我只需要让.Net从lambda中为我创建这样的表达式,然后我就可以探索它的结构。例如在调试下运行

Expression<Func<TestObject, bool>> expression = t => t.Invoice.Any(i => i.CustomerId == t.Id);

并检查表达式变量。

我假设LinqExpression来自using LinqExpression = System.Linq.Expressions.Expression;

Any没有特定的表达式类型,因为它不是运算符等。Any是一个静态方法,因此应该为此创建一个Call表达式。

您需要基于静态方法语法而不是扩展方法语法来构建表达式:

ctx.customer.Where(c => Enumerable.Any(ctx.Invoice, i => i.customerId == c.id));

要做到这一点有很多步骤。这是大纲,因为我没有时间正确地计算出所有步骤。我不完全确定如何表示内部lambda中使用的c参数不是该lambda的参数,而是外部lambda的一个参数。总之,你需要

  1. 检索MethodInfo以获取通用Enumerable.Any方法的正确重载
  2. 从泛型中构造非泛型MethodInfo(即调用MakeGenericMethod
  3. 构造将作为方法的第一个参数传递的PropertyExpression(表示ctx.Invoce
  4. 构造lambda表达式的主体,将其作为方法的第二个参数传递(即示例代码中的rightPart
  5. 构造LambdaExpression(例如var innerLambda = Expression.Lambda(rightPart, i);
  6. 构造MethodCallExpression,表示对该方法的调用,并传递MethodInfo和innerLambda
  7. 构造将传递给Where的LambdaExpression(即Expression.Lambda(methodCallExpression, c)

如果您想按照注释中的指示组合布尔表达式,则第四步会有所不同。

我希望这能有所帮助。

注意,我添加了内部表达式。您需要编写一些闭包代码,因为您有一个捕获的变量。

请注意使用中的代码时的简化机会https://stackoverflow.com/a/3472250/90475.

在DebugView中获取表达式代码的最小代码。。。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace ConsoleApplication7
{
   class CTX
   {
      public List<Customer> Invoices = new List<Customer>();
   }
   class Customer
   {
      public int id;
      public static bool HasMatchingIds(Customer first, Customer second) { return true; }
   }
   class Program
   {
      static void Main(string[] args)
      {
         CTX ctx = new CTX();
         Expression<Func<Customer, bool>> expression = cust => ctx.Invoices.Any(customer => Customer.HasMatchingIds(customer, cust));
      }
   }
}

这是我在反射器中看到的:

private static void Main(string[] args)
{
    ParameterExpression CS$0$0000;
    ParameterExpression CS$0$0002;
    CTX ctx = new CTX();
    Expression<Func<Customer, bool>> expression = Expression.Lambda<Func<Customer, bool>>(
    Expression.Call(null, (MethodInfo) methodof(Enumerable.Any),
    new Expression[] { Expression.Constant(ctx.Invoices), Expression.Lambda<Func<Customer, bool>>(
    Expression.Call(null, (MethodInfo) methodof(Customer.HasMatchingIds), new Expression[] { 
    CS$0$0002 = Expression.Parameter(typeof(Customer), "customer"),
    CS$0$0000 = Expression.Parameter(typeof(Customer), "cust") }), 
    new ParameterExpression[] { CS$0$0002 }) }), new ParameterExpression[] { CS$0$0000 });
}

距离政府工作足够近。。。这告诉我,它远非琐碎,您需要简化原始查询。

我也会尝试运行LinqPad进行快速原型

添加此代码:

var any = Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(Invoice) },
    Expression.PropertyOrField(Expression.Constant(ctx), "Invoice"),
    Expression.Lambda(rightPart, i));
var filter = Expression.Lambda<Func<Customer, bool>>(any, c);

然后可以在任何IQueryable<Customer>.Where方法中使用filter作为参数。

相关文章: