为 LINQ 查询构建带有表达式树的 Any()
Build Any() with Expression Trees for LINQ Queries
我正在使用 System.Linq.Expressions.Expression class
动态构建 SQL "Any" 子句
我可以这样做
Expression<Func<User, Lead, bool>> predicate = (user, lead) => user.UserRoleSubProducts.Any(x => x.SubProductID == lead.SubProductID);
但我无法使用表达式树实现此目的。
下面我试过了
var param1 = Expression.Parameter(typeof(User), "user");
var property1 = Expression.Property(param1, "UserRoleSubProducts");
var exp1 = Expression.Lambda(property1, new[] { param1 });
var param2 = Expression.Parameter(typeof(Lead), "lead");
var property2 = Expression.Property(param2, "SubProductID");
var exp2 = Expression.Lambda(property2, new[] { param2 });
var param3 = Expression.Parameter(property1.Type.GetProperty("Item").PropertyType, "x");
var property3 = Expression.Property(param3, "SubProductID");
var exp3 = Expression.Lambda(property3, new[] { param3 });
var equality = Expression.Equal(property2, property3);
var any = typeof(Queryable).GetMethods().Where(m => m.Name == "Any").Single(m => m.GetParameters().Length == 2).MakeGenericMethod(property1.Type);
var expression = Expression.Call(null, any, property1, equality);
但是得到
Expression of type
'Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]' cannot be used for parameter of type System.Linq.IQueryable
1[Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]]' of method 'Boolean Any[DataServiceCollection
1](System.Linq.IQueryable1[Microsoft.OData.Client.DataServiceCollection
1[Api.Models.UserRoleSubProduct]],
System.Linq.Expressions.Expression1[System.Func
2[Microsoft.OData.Client.DataServiceCollection`1[Api.Models.UserRoleSubProduct],System.Boolean]])'
我想我已经足够接近了。感谢任何帮助
忽略冗余未使用的 lambda 表达式,问题在最后 2 行。
首先,你使用了错误的泛型类型(MakeGenericMethod(property1.Type)
),而正确的类型基本上是参数的类型x
这里
.Any(x => x.SubProductID == lead.SubProductID)
=>
.Any<T>((T x) => ...)
在您的代码中映射到 param3.Type
。
其次,Any
的第二个参数必须是lambda表达式(而不是代码中的equality
)。
第三,由于 user.UserRoleSubProducts
很可能是集合类型,您应该发出对 Enumerable.Any
的调用而不是 Queryable.Any
.
Expression.Call
方法有 overload 这对于 "calling" 静态通用扩展方法非常方便:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
所以最后两行可以替换为:
var anyCall = Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new Type[] { param3.Type },
property1, Expression.Lambda(equality, param3)
);
我正在使用 System.Linq.Expressions.Expression class
动态构建 SQL "Any" 子句我可以这样做
Expression<Func<User, Lead, bool>> predicate = (user, lead) => user.UserRoleSubProducts.Any(x => x.SubProductID == lead.SubProductID);
但我无法使用表达式树实现此目的。
下面我试过了
var param1 = Expression.Parameter(typeof(User), "user");
var property1 = Expression.Property(param1, "UserRoleSubProducts");
var exp1 = Expression.Lambda(property1, new[] { param1 });
var param2 = Expression.Parameter(typeof(Lead), "lead");
var property2 = Expression.Property(param2, "SubProductID");
var exp2 = Expression.Lambda(property2, new[] { param2 });
var param3 = Expression.Parameter(property1.Type.GetProperty("Item").PropertyType, "x");
var property3 = Expression.Property(param3, "SubProductID");
var exp3 = Expression.Lambda(property3, new[] { param3 });
var equality = Expression.Equal(property2, property3);
var any = typeof(Queryable).GetMethods().Where(m => m.Name == "Any").Single(m => m.GetParameters().Length == 2).MakeGenericMethod(property1.Type);
var expression = Expression.Call(null, any, property1, equality);
但是得到
Expression of type 'Microsoft.OData.Client.DataServiceCollection
1[Api.Models.UserRoleSubProduct]' cannot be used for parameter of type System.Linq.IQueryable
1[Microsoft.OData.Client.DataServiceCollection1[Api.Models.UserRoleSubProduct]]' of method 'Boolean Any[DataServiceCollection
1](System.Linq.IQueryable1[Microsoft.OData.Client.DataServiceCollection
1[Api.Models.UserRoleSubProduct]], System.Linq.Expressions.Expression1[System.Func
2[Microsoft.OData.Client.DataServiceCollection`1[Api.Models.UserRoleSubProduct],System.Boolean]])'
我想我已经足够接近了。感谢任何帮助
忽略冗余未使用的 lambda 表达式,问题在最后 2 行。
首先,你使用了错误的泛型类型(MakeGenericMethod(property1.Type)
),而正确的类型基本上是参数的类型x
这里
.Any(x => x.SubProductID == lead.SubProductID)
=>
.Any<T>((T x) => ...)
在您的代码中映射到 param3.Type
。
其次,Any
的第二个参数必须是lambda表达式(而不是代码中的equality
)。
第三,由于 user.UserRoleSubProducts
很可能是集合类型,您应该发出对 Enumerable.Any
的调用而不是 Queryable.Any
.
Expression.Call
方法有 overload 这对于 "calling" 静态通用扩展方法非常方便:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
所以最后两行可以替换为:
var anyCall = Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new Type[] { param3.Type },
property1, Expression.Lambda(equality, param3)
);