我最近研究的另一个实现选项......
我的树很简单。
public class Node
{
public int NodeID { get; set; }
public string Name { get; set; }
public virtual Node ParentNode { get; set; }
public int? ParentNodeID { get; set; }
public virtual ICollection<Node> ChildNodes { get; set; }
public int? LeafID { get; set; }
public virtual Leaf Leaf { get; set; }
}
public class Leaf
{
public int LeafID { get; set; }
public string Name { get; set; }
public virtual ICollection<Node> Nodes { get; set; }
}
我的要求,没有那么多。
给定一组叶子和一个祖先,显示该祖先的子孙,其后代在该集合内具有叶子
一个类比是磁盘上的文件结构。当前用户有权访问系统上的文件子集。当用户打开文件系统树中的节点时,我们只想显示最终将引导他们到他们可以看到的文件的用户节点。我们不想向他们显示他们无权访问的文件的文件路径(出于安全原因,例如,泄露某种类型的文档的存在)。
我们希望能够将此过滤器表示为IQueryable<T>
,因此我们可以将其应用于任何节点查询,过滤掉不需要的结果。
为此,我创建了一个表值函数,它返回树中节点的后代。它通过 CTE 完成此操作。
CREATE FUNCTION [dbo].[DescendantsOf]
(
@parentId int
)
RETURNS TABLE
AS
RETURN
(
WITH descendants (NodeID, ParentNodeID, LeafID) AS(
SELECT NodeID, ParentNodeID, LeafID from Nodes where ParentNodeID = @parentId
UNION ALL
SELECT n.NodeID, n.ParentNodeID, n.LeafID from Nodes n inner join descendants d on n.ParentNodeID = d.NodeID
) SELECT * from descendants
)
现在,我使用的是 Code First,所以我不得不使用
https://www.nuget.org/packages/EntityFramework.Functions
为了将函数添加到我的 DbContext
[TableValuedFunction("DescendantsOf", "Database", Schema = "dbo")]
public IQueryable<NodeDescendant> DescendantsOf(int parentID)
{
var param = new ObjectParameter("parentId", parentID);
return this.ObjectContext().CreateQuery<NodeDescendant>("[DescendantsOf](@parentId)", param);
}
具有复杂的返回类型(无法重用 Node,正在调查)
[ComplexType]
public class NodeDescendant
{
public int NodeID { get; set; }
public int LeafID { get; set; }
}
当用户在树中展开一个节点时,将它们放在一起允许我获得过滤后的子节点列表。
public static Node[] GetVisibleDescendants(int parentId)
{
using (var db = new Models.Database())
{
int[] visibleLeaves = SuperSecretResourceManager.GetLeavesForCurrentUserLol();
var targetQuery = db.Nodes as IQueryable<Node>;
targetQuery = targetQuery.Where(node =>
node.ParentNodeID == parentId &&
db.DescendantsOf(node.NodeID).Any(x =>
visibleLeaves.Any(y => x.LeafID == y)));
// Notice, still an IQueryable. Perform whatever processing is required.
SortByCurrentUsersSavedSettings(targetQuery);
return targetQuery.ToArray();
}
}
重要的是要注意该函数是在服务器上执行的,而不是在应用程序中执行的。这是执行的查询
SELECT
[Extent1].[NodeID] AS [NodeID],
[Extent1].[Name] AS [Name],
[Extent1].[ParentNodeID] AS [ParentNodeID],
[Extent1].[LeafID] AS [LeafID]
FROM [dbo].[Nodes] AS [Extent1]
WHERE ([Extent1].[ParentNodeID] = @p__linq__0) AND ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent2].[LeafID] AS [LeafID]
FROM [dbo].[DescendantsOf]([Extent1].[NodeID]) AS [Extent2]
) AS [Project1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
WHERE [Project1].[LeafID] = 17
)
))
注意上面查询中的函数调用。