【问题标题】:Linq to SQL Nested ObjectsLinq to SQL 嵌套对象
【发布时间】:2011-03-14 15:50:46
【问题描述】:
我有一个名为 Category 的对象,它有一个 Id、Name 和 OwnerId。然后我嵌套这些以创建子类别。如果一个类别有一个所有者 ID,那么它就是一个子类别。子类别的数量是无限的,但每个项目只能有 1 个父项。很简单。
我的问题是,我需要在加载后访问子类别。如何使用 Linq 获取拥有类别。我知道所有者 ID,但我不知道所有者可以有多少 lvls。
基本上,我正在寻找一种方法来获取 Id == X 的类别或子类别,但这可以存在于 6 个级别或更深的子类别中。
我试图避免每个子类别中的每个子类别出现循环......
【问题讨论】:
标签:
c#
.net
linq
performance
linq-to-sql
【解决方案1】:
this fogbugz 博客文章中解释了另一种存储/检索树层次结构的方法:
原来有一个很酷
此问题的解决方案由
乔·塞尔科。而不是试图
维护一堆父母/孩子
整个数据库中的关系
-- 这将需要递归 SQL 查询来查找所有
一个节点的后代——我们标记每个
具有“左”和“右”值的情况
通过遍历树计算
深度优先,边走边算。一种
节点的“左”值在任何时候被设置
在遍历期间首次出现,并且
行走时设置“正确”值
将树备份到远离节点的位置。
图片可能更有意义:
嵌套集 SQL 模型让我们可以添加
不牺牲案例层次结构
性能。
这有什么帮助?现在我们只问
对于所有具有“左”值的情况
在 2 到 9 之间找到所有
B的后代在一个快速,索引
询问。 G的祖先由
要求“左”小于的节点
6(G自己的“左”)和“右”更大
超过 6. 适用于所有数据库。
大大提高性能——
特别是在查询大
层次结构
Here's another post 更详细。它是使用 Sql 和 php 编写的,但我认为您可以了解它的要点并轻松地将 Linq 转换为 Sql。
【解决方案2】:
在 MS SQL 2005 及更高版本中,您可以创建递归查询。但是,在 LINQ to SQL 中,您不走运。如果不重构数据库中的数据,就无法在单个数据库调用中遍历树。
但是...我能想到 1 种解决方法。当您能够将单个树(或树的一部分)的所有 Category 元素组合在一起时,您可以在单个语句中预加载整个树的该部分。之后,您将能够遍历树的该部分,而不会触发对数据库的新调用。它看起来像这样:
// Load the category that will be used as starting point.
var subCategory = db.Categories.Single(c => c.Id == 56);
// Performance: Load the complete group in one go.
var categories = (
from category in db.Categories
where category.GroupId == subCategory.GroupId
select category)
.ToArray();
// Traverse the tree and get the top-most parent (if any).
var parent = subCategory.GetParents().LastOrDefault();
// Extension method to get the parents.
public static IEnumerable<Category> GetParents(
this Category category)
{
while (category.Parent != null)
{
// NOTE: cat.Parent will not cause a database call
// when the Parent is already loaded by L2S.
yield return cat.Parent;
category = category.Parent;
}
}
这当然只有在您能够将元素确定为一个组时才有效。此解决方案是否会更快还取决于组的大小。当您加载(并且不使用)的对象组非常大时,它实际上会减慢应用程序的速度。