對於給定的搜索標籤過濾器,預期結果是表示在給定標籤ID列表中具有所有標籤的實體的表達式。
Lambda可能將此表達為:
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));
嘗試將其表示為表達式樹失敗:
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
);
類型'System.Linq.Enumerable'上沒有泛型方法'All'與提供的類型參數和參數兼容。如果方法是非泛型的,則不應提供類型參數。
有人認為它可以作為Enumerable.All<TSource, Func<TSource, bool>>
應該由searchTagsConstant
和anyCall
滿足嗎?
t被認為它將作為
Enumerable.All<TSource, Func<TSource, bool>>
應該由searchTagsConstant
和anyCall
滿足
不。 anyCall
不是lambda表達式( Func<TSource, bool>
),只是這種表達式的潛在體 。
讓我們從你的目標開始:
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));
學習如何構建表達式樹的最簡單方法是在編譯時創建一個示例表達式,並通過調試器在運行時通過某個反編譯器或表達式樹檢查生成的代碼。
無論如何,請注意上面的表達式有3個lambda參數,而在你的嘗試中你只有2個。還沒有Contains
調用,不知道你為什麼放入那裡。
構建上面的表達式可以是這樣的:
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);