Как вы моделируете древовидную иерархию в Entity Framework?

#.net #sql-server-2008 #entity-framework

#.net #sql-server-2008 #entity-framework

Вопрос:

Мне было поручено создать новую систему, которая моделирует виртуальную файловую систему. Мой клиент обязал, чтобы это было построено с использованием Entity Framework. Я раньше обрабатывал узлоподобные структуры данных, но никогда с Entity Framework.

Какой наилучший подход к созданию иерархического класса в Entity Framework? Под иерархическим я подразумеваю, что класс может иметь родительского элемента того же типа и может иметь ноль или более дочерних элементов того же типа.

Я использую SQL Server 2008 и Entity Framework 4.0. Должен ли я использовать встроенный тип данных иерархии или мне следует использовать родительский идентификатор? Предложения будут только приветствоваться.

Ответ №1:

У меня была та же проблема. Я обнаружил, что лучший способ работать с типом данных hierarchyid и при этом использовать EF 4.0 — это создать представление по таблице иерархии.

Поскольку представление не подлежит обновлению, я создал хранимые процедуры удаления, вставки и добавления и сопоставил их с отображением объектов в ORM. Это работает действительно хорошо.

Допустим, у вас есть эта таблица:

 CREATE TABLE [dbo].[NodeHierarchy]
(
[Node] hierarchyid NOT NULL,
[NodeId] int NOT NULL,
[Level] AS ([Node].[GetLevel]()) PERSISTED,
[Lineage] AS ([Node].[ToString]()) PERSISTED,
[RootNode] AS ([Node].[GetAncestor]([Node].[GetLevel]() - 1)) PERSISTED,
[ParentNode] AS ([Node].[GetAncestor](1)) PERSISTED
)
  

Теперь вы создаете это представление над ним:

 CREATE VIEW [dbo].[NodeHierarchyView]
AS
SELECT   ch.NodeId AS [NodeId],
       ch.Node.ToString() AS [Lineage],
 ch.[Level] AS [Level],
 chr.Node.ToString() AS [RootLineage],
 chr.NodeId AS [RootNodeId],
 chp.Node.ToString() As [ParentLineage],
 chp.NodeId AS [ParentNodeId]
FROM     dbo.NodeHierarchy ch
 LEFT OUTER JOIN NodeHierarchy chr ON
      ch.RootNode = chr.Node
 LEFT OUTER JOIN CompanyHierarchy chp ON
      ch.ParentNode = chp.Node
  

Теперь я могу создать объект в модели поверх представления и использовать Linq-to-Entities и получить хорошую производительность и аккуратный код.

Это хранимая процедура add, которую я использую:

 CREATE PROCEDURE [dbo].[AddNode]
@NodeId int,
@ParentNodeId int
AS
DECLARE @NewNode hierarchyid;
DECLARE @ParnetLineage nvarchar(4000);

SELECT  @ParnetLineage = Lineage
FROM    NodeHierarchy
WHERE   NodeId = @ParentNodeId

IF @ParnetLineage IS NULL
BEGIN
    SET @ParnetLineage = N'/';
END

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
    SET @NewNode = CAST(@ParnetLineage   CAST(@NodeId AS nvarchar (4000))   N'/' AS hierarchyid);

    INSERT NodeHierarchy (Node, NodeId)
    VALUES (@NewNode, @NodeId)
COMMIT

SELECT @NodeId AS [NewNodeId]
RETURN 0
  

Я создал все необходимые индексы и ограничения для таблиц. В моем решении представление отображает данные из других таблиц, и процедуры также управляют этими таблицами.

Oded

Ответ №2:

Вы должны использовать ParentID, потому что тип данных hierarchyid не поддерживается EF (вы также можете проверить описанный обходной путь). В любом случае будьте готовы к написанию хранимых процедур, потому что загрузка иерархий с помощью EF обычно сложна.