Construire Any () avec des arbres d'expression pour les requêtes LINQ

c# expression-trees linq

Question

Je construis une clause SQL "Any" de manière dynamique à l'aide de la classe System.Linq.Expressions.Expression

Je peux le faire comme ça

Expression<Func<User, Lead, bool>> predicate = (user, lead) => user.UserRoleSubProducts.Any(x => x.SubProductID == lead.SubProductID);

Mais je ne parviens pas à atteindre cet objectif avec Expression Tree.

J'ai essayé ci-dessous

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

Mais obtenir

L'expression de type 'Microsoft.OData.Client.DataServiceCollection 1[Api.Models.UserRoleSubProduct]' cannot be used for parameter of type System.Linq.IQueryable 1 [Microsoft.OData.Client.DataServiceCollection 1[Api.Models.UserRoleSubProduct]]' of method 'Boolean Any[DataServiceCollection 1] (System.Linq.IQueryable 1[Microsoft.OData.Client.DataServiceCollection 1 [Api.Models.UserRoleSubProduct]], System.Linq.Expressions.Expression 1[System.Func 2 [Microsoft .OData.Client.DataServiceCollection`1 [Api.Models.UserRoleSubProduct], System.Boolean]]) '

Je pense que je suis assez proche. Toute aide est appréciée

Réponse acceptée

En ignorant les expressions lambda inutilisées et redondantes, le problème réside dans les deux dernières lignes.

Tout d'abord, vous utilisez un type générique incorrect ( MakeGenericMethod(property1.Type) ), alors que le type correct est fondamentalement le type du paramètre x ici

.Any(x => x.SubProductID == lead.SubProductID)

=>

.Any<T>((T x) => ...)

qui correspond à param3.Type dans votre code.

Deuxièmement, le deuxième argument de Any doit être une expression lambda (pas simplement une equality comme dans le code).

Troisièmement, puisque user.UserRoleSubProducts très probablement un type de collection, vous devez émettre un appel à Enumerable.Any plutôt qu'à Queryable.Any .

La méthode Expression.Call a surcharge, ce qui est très pratique pour "appeler" des méthodes d'extension génériques statiques:

public static MethodCallExpression Call(
    Type type,
    string methodName,
    Type[] typeArguments,
    params Expression[] arguments
)

Donc, les deux dernières lignes peuvent être remplacées par:

var anyCall = Expression.Call(
    typeof(Enumerable), nameof(Enumerable.Any), new Type[] { param3.Type },
    property1, Expression.Lambda(equality, param3)
);


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi