Comment exprimer Tous ont n'importe quel arbre d'expression

c# expression-trees linq

Question

Pour un filtre d'étiquettes de recherche donné, le résultat attendu est une expression représentant des entités avec Toutes les étiquettes dans une liste donnée d'identificateurs d'étiquettes.

Un Lambda pourrait exprimer ceci comme:

class Tag 
{
   public long TagId { get; set; }
}

class Taggable 
{
   ICollection<Tag> Tags { get; set; }
}

...

IEnumerable<long> searchTags = new List<long>() { 1, 2, 3 };
Func<Taggable, bool> filter = taggable => searchTags.All(qtag => taggable.Tags.Any(tag => tag.TagId == qtag));

Une tentative pour représenter cela comme un arbre d'expression échoue:

var tagParam = Expression.Parameter(typeof(Tag), "tag");    
var taggableParam = Expression.Parameter(typeof(Taggable), "taggable");
MemberExpression tagsProperty = Expression.Property(taggableParam, "Tags");
ConstantExpression searchTagsConstant = Expression.Constant(searchTags);

var containsCall = Expression.Call(
      typeof(Enumerable), "Contains",
      new[] { typeof(long) },
      searchTagsConstant,
      Expression.Property(tagParam, "TagID")
);

var anyCall = Expression.Call(
     typeof(Enumerable), "Any",
     new[] { typeof(Tag) },
     tagsProperty,
     Expression.Lambda(containsCall, tagParam)
);

// FAILS HERE
var allCall = Expression.Call(
    typeof(Enumerable), "All",
    new[] { typeof(long) },
    searchTagsConstant,
    anyCall
);

Aucune méthode générique 'All' sur le type 'System.Linq.Enumerable' n'est compatible avec les arguments de type fournis. Aucun argument de type ne doit être fourni si la méthode est non générique.

On pensait que cela fonctionnerait comme Enumerable.All<TSource, Func<TSource, bool>> devraient être satisfaits par searchTagsConstant et anyCall ?

Réponse acceptée

On pensait que cela fonctionnerait comme Enumerable.All<TSource, Func<TSource, bool>> devraient être satisfaits par searchTagsConstant et anyCall

Nan. anyCall n'est pas une expression lambda ( Func<TSource, bool> ), mais seulement un corps potentiel d'une telle expression.

Laisser partir de votre cible:

IEnumerable<long> searchTags = new List<long>() { 1, 2, 3 };

Expression<Func<Taggable, bool>> lambda = 
    taggable => searchTags.All(searchTag => taggable.Tags.Any(tag => tag.TagId == searchTag));

Le moyen le plus simple d'apprendre à créer une arborescence d'expression consiste à créer un exemple d'expression lors de la compilation et à examiner le code généré via un décompilateur ou l'arborescence d'expression au moment de l'exécution via le débogueur.

Quoi qu'il en soit, notez que l'expression ci-dessus a 3 paramètres lambda, alors que dans votre tentative, vous n'en avez que 2. De plus, il n'y a pas d'appel Contains , vous ne savez pas pourquoi vous y avez entré.

Construire l'expression ci-dessus peut être comme ceci:

var taggable = Expression.Parameter(typeof(Taggable), "taggable");
var tag = Expression.Parameter(typeof(Tag), "tag");
var searchTag = Expression.Parameter(typeof(long), "searchTag");
// tag.TagId == searchTag
var anyCondition = Expression.Equal(
    Expression.Property(tag, "TagId"),
    searchTag);
// taggable.Tags.Any(tag => tag.TagId == searchTag)
var anyCall = Expression.Call(
    typeof(Enumerable), nameof(Enumerable.Any), new[] { typeof(Tag) },
    Expression.Property(taggable, "Tags"), Expression.Lambda(anyCondition, tag));
// searchTags.All(searchTag => taggable.Tags.Any(tag => tag.TagId == searchTag))
var allCall = Expression.Call(
    typeof(Enumerable), nameof(Enumerable.All), new[] { typeof(long) },
    Expression.Constant(searchTags), Expression.Lambda(anyCall, searchTag));
// taggable => searchTags.All(searchTag => taggable.Tags.Any(tag => tag.TagId == searchTag))
var lambda = Expression.Lambda(allCall, taggable);


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