如何用表达式树表达 All have Any
How to express All have Any with Expression Trees
对于给定的搜索标签过滤器,预期结果是一个表达式,表示具有给定标签 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
);
No generic method 'All' on type 'System.Linq.Enumerable' is compatible
with the supplied type arguments and arguments. No type arguments
should be provided if the method is non-generic.
人们认为它会起作用,因为 Enumerable.All<TSource, Func<TSource, bool>>
应该由 searchTagsConstant
和 anyCall
满足?
t was thought it would work as the Enumerable.All<TSource, Func<TSource, bool>>
should be satisfied by the searchTagsConstant
and anyCall
没有。 anyCall
不是 lambda 表达式 (Func<TSource, bool>
),只是这种表达式的潜在 body。
让我们从您的目标开始:
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);
对于给定的搜索标签过滤器,预期结果是一个表达式,表示具有给定标签 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
);
No generic method 'All' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
人们认为它会起作用,因为 Enumerable.All<TSource, Func<TSource, bool>>
应该由 searchTagsConstant
和 anyCall
满足?
t was thought it would work as the
Enumerable.All<TSource, Func<TSource, bool>>
should be satisfied by thesearchTagsConstant
andanyCall
没有。 anyCall
不是 lambda 表达式 (Func<TSource, bool>
),只是这种表达式的潜在 body。
让我们从您的目标开始:
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);