有没有办法使用LINQ/EF来获取父/子层次结构中最顶层的项?
我有一个叫做有没有办法使用LINQ/EF来获取父/子层次结构中最顶层的项?,linq,c#-4.0,entity-framework-4,lambda,Linq,C# 4.0,Entity Framework 4,Lambda,我有一个叫做Structure的类: public class Structure { public int StructureId { get; set; } public Structure Parent { get; set; } } 如您所见,Structure有一个父Structure。在这个层次结构中可以有无限数量的结构 是否有任何方法可以使用LINQ(带实体框架)来获取此层次结构中最顶层的结构 目前,我必须多次访问数据库才能找到最顶尖的家长。最顶端的父级是一个结构
Structure
的类:
public class Structure
{
public int StructureId { get; set; }
public Structure Parent { get; set; }
}
如您所见,Structure
有一个父Structure
。在这个层次结构中可以有无限数量的结构
是否有任何方法可以使用LINQ(带实体框架)来获取此层次结构中最顶层的结构
目前,我必须多次访问数据库才能找到最顶尖的家长。最顶端的父级是一个结构
,具有空的父级
属性:
Structure structure = structureRepository.Get(id);
while (structure.Parent != null)
{
structure = structureRepository.Get(structure.Parent.StructureId);
}
// When we're here; `structure` is now the top most parent.
那么,使用LINQ/Lambdas有什么优雅的方法可以做到这一点吗?理想情况下,从以下代码开始:
var structureQuery = from item in context.Structures
where item.StructureId == structureId
select item;
我只希望能够编写如下内容,这样我只需点击一次数据库:
structureQuery = Magic(structureQuery);
Structure topMostParent = structureQuery.Single();
我喜欢这个问题,想不出一个简单的方法。但是,您是否可以在您的存储库类上实现这一点?毕竟,顶部应该只有一个,如果需要它,那么它可能需要一个
structureRepository.GetRoot()
之类的东西。例如,您可以使用linq-take构造
var first3Customers = (
from c in customers
select new {c.CustomerID, c.CustomerName} )
.Take(2);
我也有类似的情况。我没有设法直接用LINQ/EF解决它。相反,我通过使用递归公共表表达式创建数据库视图来解决这个问题,如前所述。我创建了一个用户定义函数,将所有父对象交叉应用于子对象(反之亦然),然后创建了一个视图,该视图使用我导入到EF对象上下文中的用户定义函数 (免责声明:简化代码,我没有实际测试) 我有两个表,比如MyTable(包含所有项)和MyParentChildTable,其中包含ChildId、ParentId关系 然后,我定义了以下udf:
CREATE FUNCTION dbo.fn_getsupertree(@childid AS INT)
RETURNS @TREE TABLE
(
ChildId INT NOT NULL
,ParentId INT NULL
,Level INT NOT NULL
)
AS
BEGIN
WITH Parent_Tree(ChildId, ParentId)
AS
(
-- Anchor Member (AM)
SELECT ChildId, ParentId, 0
FROM MyParentChildTable
WHERE ChildId = @childid
UNION all
-- Recursive Member (RM)
SELECT info.ChildId, info.ParentId, tree.[Level]+1
FROM MyParentChildTable AS info
JOIN Parent_Tree AS tree
ON info.ChildId = tree.ParentId
)
INSERT INTO @TREE
SELECT * FROM Parent_Tree;
RETURN
END
以及以下观点:
CREATE VIEW VwSuperTree AS (
SELECT tree.*
FROM MyTable
CROSS APPLY fn_getsupertree(MyTable.Id) as tree
)
GO
这给了我每个孩子,所有的父母都有他们的“树级别”(直接父母有级别1,父母的父母有级别2,等等)。从该视图中,可以轻松查询具有最高级别的项。我刚刚在EF上下文中导入了视图,以便能够使用LINQ查询它 这不是一个直接的答案,但您遇到的问题与存储树的方式有关。有两种方法可以通过不同的数据结构简化此查询 一种是使用一个可以简化跨树的多种查询的方法
另一种方法是存储祖先/后代/深度元组的非规范化表。然后,此查询将查找具有当前结构的元组作为具有最大深度的子体。我认为最好的方法是从我想要的顶部父结构中一次加载整个层次结构:
var structureQuery = from item in context.Structures
.Include(x => x.Parent)
where item.StructureId == structureId
select item;
然后只需使用代码:
while (structure.Parent != null)
{
structure = structure.Parent;
}
我认为这是不可能的,但我对其他人可能提出的解决方案感到好奇…@Nick-mssql2005@杰罗恩-是的,我也不这么认为。我编写了当前执行此任务的存储过程,它本身会多次访问数据库;也许你应该删除它,并将其表示为一条评论:)。是的,我已经有了类似的东西。我目前正在尝试将尽可能多的存储过程转移到EF。这可能是其中一个必须作为存储过程保留的过程。我使用的是MS SQL 2005。以前从未听说过NSHs。@GenericTypeTea这不是一个数据库引擎功能,只是一种构造数据的方法,以便可以使用标准SQL轻松查询数据。以下是有关SQL Server的讨论: