I'm building a SQL "Any" clause dynamically using the System.Linq.Expressions.Expression class
I can do it like this
Expression<Func<User, Lead, bool>> predicate = (user, lead) => user.UserRoleSubProducts.Any(x => x.SubProductID == lead.SubProductID);
But I am not able to achieve this using Expression Tree.
I have tried below
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);
But getting
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]])'
I think I am close enough. Any help is appreciated
Ignoring the redundant unused lambda expressions, the problem is in the last 2 lines.
First, you are using a wrong generic type (MakeGenericMethod(property1.Type)
), while the correct type is basically the type of the parameter x
here
.Any(x => x.SubProductID == lead.SubProductID)
=>
.Any<T>((T x) => ...)
which maps to param3.Type
in your code.
Second, the second argument of the Any
must be lambda expression (not simply equality
as in the code).
Third, since user.UserRoleSubProducts
most likely is a collection type, you should emit call to Enumerable.Any
rather than Queryable.Any
.
The Expression.Call
method has overload which is very handy for "calling" static generic extension methods:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
So the last two lines can be replaced with:
var anyCall = Expression.Call(
typeof(Enumerable), nameof(Enumerable.Any), new Type[] { param3.Type },
property1, Expression.Lambda(equality, param3)
);