Per un determinato filtro dei tag di ricerca, il risultato previsto è un'espressione che rappresenta le entità con Tutti i tag in un determinato elenco di ID di tag.
Un Lambda potrebbe esprimere questo come:
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));
Un tentativo di rappresentarlo come un albero di espressioni non riesce:
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
);
Nessun metodo generico 'Tutti' sul tipo 'System.Linq.Enumerable' è compatibile con gli argomenti e gli argomenti del tipo fornito. Non è necessario fornire argomenti di tipo se il metodo non è generico.
Si pensava che avrebbe funzionato come Enumerable.All<TSource, Func<TSource, bool>>
dovrebbe essere soddisfatto dal searchTagsConstant
e anyCall
?
Si pensava che avrebbe funzionato come
Enumerable.All<TSource, Func<TSource, bool>>
dovrebbe essere soddisfatto dalsearchTagsConstant
eanyCall
No. anyCall
non è un'espressione lambda ( Func<TSource, bool>
), solo un potenziale corpo di tale espressione.
Cominciamo dal tuo obiettivo:
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));
Il modo più semplice per imparare a costruire un albero di espressioni è creare un'espressione di esempio in fase di compilazione ed esaminare il codice generato tramite un decompilatore o l'albero delle espressioni in fase di esecuzione tramite debugger.
Ad ogni modo, nota che l'espressione sopra ha 3 parametri lambda, mentre nel tuo tentativo ne hai solo 2. Inoltre non c'è chiamata Contains
, non sono sicuro del motivo per cui ci hai messo dentro.
Costruire l'espressione sopra può essere così:
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);