【发布时间】:2020-11-04 12:28:12
【问题描述】:
以下是我的实际情况的简化版。假设我有这个Person 实体:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
// etc.
// Some navigation properties
public virtual ICollection<Thing> Things { get; set; }
// etc.
}
我还写了一个扩展方法来根据一个或多个属性进行过滤:
public static IQueryable<Person> Filter(this IQueryable<Person> query,
string name = null, int? thingId = null,
Foo etc = null)
{
if (!string.IsNullOrEmpty(name))
query = query.Where(p => p.Name.ToLower().Contains(name.ToLower()));
if (thingId.HasValue)
query = query.Where(p => p.Things.Count > 0 &&
p.Things.Any(t => t.Id == thingId.Value));
// etc.
return query;
}
..我可以这样使用:
var query = context.People.Filter(name, thingId);
var filteredPeople = query.Include(p => p.Things).Include(__).OrderBy(__).ToList();
我想让Person 成为一个嵌套实体(即每个人都有一个人的集合)。所以,我添加了以下属性:
public virtual ICollection<Person> Children { get; set; }
[ForeignKey("Parent")]
public int? ParentId { get; set; }
public virtual Person Parent { get; set; }
现在我正在努力实现过滤逻辑。我需要的是:
- 如果父
Person匹配过滤器,则将包含它或如果它的后代之一匹配。 - 只有满足上述条件的子
Person才会被包括在内。
对于第二个问题,我可能会尝试this answer 中的解决方案,但我需要先解决第一个问题。我试图创建一个递归表达式,如下所示:
private static IQueryable<Person> FilterByName(this IQueryable<Person> query, string name)
{
if (string.IsNullOrEmpty(name)) return query;
Expression<Func<Person, bool>> selector = (p) =>
p.Name.ToLower().Contains(name.ToLower())
|| p.Children.AsQueryable().FilterByName(name).Any();
return query.Where(selector);
}
..但我得到一个例外,说它“无法翻译成商店表达式”。
我能想到的唯一其他解决方案是递归迭代子树并尝试手动构建列表,因为它需要太多查询,所以效率低下。
如何有效过滤Person 及其后代的集合?
【问题讨论】:
-
不要尝试对 EF 进行此操作。这是一个递归 CTE 查询,任何当前的 EF 版本都不支持。所以最好通过 Dapper 运行 SQL。
-
altho Svyatoslav 的评论可能不是你想听到的那种正确的评论。或者至少我不知道这样做......相反我在代码中执行
recursively iterate the children's tree......所以你需要你的数据库来理解父子和链接......我这样做的方式是添加一个parentId到 Person 表,然后在代码中您可以构建一个树模型....然后应用您的逻辑,但您将无法直接从 EF 生成查询来为您构建它....或者其他任何东西,您需要更多的非关系数据库......如果是这样的话,即使我不确定。 -
@Seabizkit 谢谢。我现在可能不得不在代码中加载它。我只是想让这个问题得到更多关注,以防有一种我想不出的方法,因此,赏金。
-
我首先将 p.Children.AsQueryable().FilterByName(name) 替换为 FilterByName 的内容,以查看 EF 将生成哪个查询(如果有的话)(没有单独方法的过滤也是如此,到位)。
标签: c# entity-framework linq entity-framework-6