4

我想动态生成一个可以用作过滤器的 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")

请协助。

4

4 回答 4

8

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

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

并检查表达式变量。

于 2012-03-27T14:52:32.633 回答
5

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

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

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

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

有很多步骤。这里是大纲,因为我没有时间正确完成所有步骤。我不完全确定如何表示c内部 lambda 中使用的参数不是该 lambda 的参数,而是外部 lambda 的参数。概括地说,你需要

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

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

我希望这会有所帮助。

于 2012-03-27T14:52:51.163 回答
2

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

请注意使用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 进行快速原型设计

于 2012-03-27T15:05:26.440 回答
1

添加此代码:

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

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

于 2012-03-27T15:42:54.210 回答