执行延迟查询以识别所有后代?

Posted

技术标签:

【中文标题】执行延迟查询以识别所有后代?【英文标题】:Execution-Deferred query to identify all descendants? 【发布时间】:2016-05-07 12:31:20 【问题描述】:

所以我有一个看起来像这样的简单表格来表示支持动态 N 级深度的类别和子类别树。

CategoryID int NOT NULL (PK)
ParentCategoryID int NULLABLE (FK to self)
CategoryName varchar(100) NOT NULL

我的实体(凭记忆打出来的,如果这里有愚蠢的错误,请见谅):

public class Category

    public int CategoryId  get; set; 
    public int ParentCategoryId  get; set; 
    public string CategoryName  get; set; 

    public virtual Category ParentCategory  get; set; 
    public IDbSet<Category> ImmediateChildCategories  get; set; 

在 C# (4.5+) 的实体框架 (6.x) 中使用延迟执行的 lambda 表达式,我将如何识别作为指定类别后代的所有类别?

我想要的伪代码 SQL 查询是这样的:

SELECT * FROM Category WHERE AnyLevelOfAncestorId = 123;

我希望看到的伪代码 EF 查询是这样的(分页是为了强调我需要执行延迟支持):

_db.Categories.Where(cat => cat.HasAncestor(123)).Skip(1000).Take(25).ToList();

其他细节:

ParentCategoryID 为 NULL 的类别是树的根节点(可以有多个)。 任何类别都不会有自己的 CategoryID 或任何后代的 ID 作为 ParentCategoryID(即没有循环关系和 所有关系 最终终止于根节点,尽管我可能会查询低于根节点的 ID ) 我不确定是否要在结果中包含指定的 ID (123)。因此,一旦我知道我是否想要包含特定的答案,我会接受任何一种方式的答案并根据需要进行调整。

【问题讨论】:

我不相信 EF 支持递归查询的生成。我通常会通过在数据库中定义一个递归视图来解决这个问题,该视图投影所有类别-后代对,然后从 EF 查询或加入该视图。如何定义递归视图取决于 DBMS。 SQL Server 有recursive CTEs;我不了解甲骨文。 @Douglas 我有一个 DBA 向我展示了一些 P-SQL 语法,它使用 START WITHCONNECT BY PRIORGROUP BY ROLLUP 在单个查询中执行此操作。 EF 没有办法使用这样的结构来投射查询吗?如果必须,我们会将其捆绑在存储过程中,但我希望我们可以避免这种情况,因为维护它们对我们来说是一种痛苦(如果这是我们必须采取的方式,视图也是如此)。 显然,较新版本的 Oracle 也支持递归 CTE,所以我建议您使用这些。它们比存储过程更易于维护,并且允许进一步的查询组合(根据您的要求)。 推荐递归 CTE 的相关答案:***.com/a/11929928/1149773。 Oracle 中的递归:***.com/a/4659866/1149773. @Douglas 我认为那些 CTE 链接是我需要的答案。不完全是我正在寻找的东西,但目前看来我正在寻找的东西对于 EF 来说是不可能的。我将编辑您的答案以包含该信息并将其标记为答案。谢谢! 【参考方案1】:

我假设您的 Category 实体类型具有用于检索其子类别的集合导航属性(通过外键相关)。此导航属性将导致子类别在第一次访问时延迟加载。您可以在根及其子项上定义递归调用此导航属性(例如ChildCategories)的方法。

public static IEnumerable<Category> GetDescendants(Category root)

    return root.ChildCategories.Concat(root.ChildCategories.SelectMany(GetDescendants));

上述代码的缺点是它会发出一个单独的数据库查询来检索每个父母的孩子。我不相信实体框架目前支持生成单个递归查询(EF 6.1.3)。相反,我建议您在数据库中 定义一个递归视图来投影所有类别-后代对;将此视图作为实体包含在您的实体数据模型中;然后从您的 LINQ 查询中查询或加入它。可以使用特定于 DBMS 的技术来定义视图。 SQL Server 支持recursive CTEs,recent versions of Oracle 也是如此。

【讨论】:

看起来我必须沿着递归 CTE 或其他路径进行递归查询。谢谢@Douglas!

以上是关于执行延迟查询以识别所有后代?的主要内容,如果未能解决你的问题,请参考以下文章

获取 JavaScript 以识别在 HTML 下拉菜单中选择了哪个选项并返回运行 SQL 查询的 PHP 函数

如何获取嵌套子查询以识别父查询列

我如何准确识别O(nlogn)?

查询以识别公司高于平均销售额

验证码识别API,识别率95%,超低延迟,支持高并发,永久免费,长期有效!

识别一个四位数的所有排列