如何生成一个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")
请协助。
当我需要手动创建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的一个参数。总之,你需要
- 检索MethodInfo以获取通用
Enumerable.Any
方法的正确重载 - 从泛型中构造非泛型MethodInfo(即调用
MakeGenericMethod
) - 构造将作为方法的第一个参数传递的PropertyExpression(表示
ctx.Invoce
) - 构造lambda表达式的主体,将其作为方法的第二个参数传递(即示例代码中的
rightPart
) - 构造LambdaExpression(例如
var innerLambda = Expression.Lambda(rightPart, i);
) - 构造MethodCallExpression,表示对该方法的调用,并传递MethodInfo和innerLambda
- 构造将传递给
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
作为参数。