Обходные решения для платформы Entity Framework - программирование
Подтвердить что ты не робот

Обходные решения для платформы Entity Framework

EF 5.0

Я работаю над прототипом для одновременного тестирования структуры иерархии и сущности. У меня есть следующая схема:

Create Table dbo.Employee
(
   EmployeeId int identity not null,
   Name nvarchar(100) not null,
   Node hierarchyid not null,
   NodePath as Node.ToString() persisted,
   Level AS Node.GetLevel() persisted,
   ManagerNode as Node.GetAncestor(1) persisted,
   ManagerNodePath as Node.GetAncestor(1).ToString() persisted
);

Alter Table dbo.Employee
    Add Constraint EmployeePK Primary Key NonClustered (EmployeeId);

Go

--Enforce Hierarchy
Alter Table dbo.Employee
    Add Constraint EmployeeManagerNodeNodeFK Foreign Key (ManagerNode) References Employee(Node);
Go

Create Unique Clustered Index EmployeeDepthFirstIndex on dbo.Employee(Node);

Go

Create NonClustered Index EmployeeBreathFirstIndex on dbo.Employee(Level, Node);

Go

Из моего чтения тип данных hierarchyid в настоящее время не поддерживается в EF, но некоторые из них предложили обходные пути, такие как создание вычисленных столбцов (Node.ToString()), которые я сделал выше.

Есть ли способ настроить EF так, чтобы он распознавал отношения родителя/ребенка, чтобы я мог эффективно иметь коллекцию подчиненных? например.

Employee.Subordinates

Единственное, о чем я могу думать, это создать столбец ManagerId с FK, но затем я эффективно храню иерархию в двух местах.

Спасибо за любую помощь!

4b9b3361

Ответ 1

EF6 теперь является открытым исходным кодом, поэтому легко добавить поддержку HierarcyID. Я добавил это тоже. Вы можете загрузить измененный источник и соответствующие/подписанные dll из codeplex: http://entityframework.codeplex.com/SourceControl/network/forks/zgabi/efhierarchyidrc1 (иногда меняется имя вилки) Или из NuGet: https://www.nuget.org/packages/EntityFrameworkWithHierarchyId/ Currenty EF6 находится в состоянии RC1, но я буду объединять модификации с каждым последующим выпуском EF6.

У меня есть следующая модель:

public class Employee
{
    public int EmployeeId { get; set; }
    [Required, MaxLength(100)]
    public string Name { get; set; }
    [Required]
    public HierarchyId Node { get; set; }

    public IQueryable<Employee> GetSubordinates(MyContext context)
    {
        return context.Employees.Where(o => Node == o.Node.GetAncestor(1));
    }
}

public class MyContextInitializer : CreateDatabaseIfNotExists<MyContext>
{
    protected override void Seed(MyContext context)
    {
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD [ManagerNode] AS ([Node].[GetAncestor]((1))) PERSISTED");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD CONSTRAINT [UK_EmployeeNode] UNIQUE NONCLUSTERED (Node)");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees]  WITH CHECK ADD CONSTRAINT [EmployeeManagerNodeNodeFK] " +
            "FOREIGN KEY([ManagerNode]) REFERENCES [dbo].[Employees] ([Node])");
        context.Employees.Add(new Employee { Name = "Root", Node = new HierarchyId("/") });
        context.Employees.Add(new Employee { Name = "Emp1", Node = new HierarchyId("/1/") });
        context.Employees.Add(new Employee { Name = "Emp2", Node = new HierarchyId("/2/") });
        context.Employees.Add(new Employee { Name = "Emp3", Node = new HierarchyId("/1/1/") });
        context.Employees.Add(new Employee { Name = "Emp4", Node = new HierarchyId("/1/1/1/") });
        context.Employees.Add(new Employee { Name = "Emp5", Node = new HierarchyId("/2/1/") });
        context.Employees.Add(new Employee { Name = "Emp6", Node = new HierarchyId("/1/2/") });
    }
}

public class MyContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
}

Сгенерированная база данных:

CREATE TABLE [dbo].[Employees](
    [EmployeeId] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](100) NOT NULL,
    [Node] [hierarchyid] NOT NULL,
    [ManagerNode]  AS ([Node].[GetAncestor]((1))) PERSISTED,
 CONSTRAINT [PK_dbo.Employees] PRIMARY KEY CLUSTERED 
(
    [EmployeeId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
 CONSTRAINT [UK_EmployeeNode] UNIQUE NONCLUSTERED 
(
    [Node] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

ALTER TABLE [dbo].[Employees]  WITH CHECK ADD  CONSTRAINT [EmployeeManagerNodeNodeFK] FOREIGN KEY([ManagerNode])
REFERENCES [dbo].[Employees] ([Node])

Пример получения дочерних узлов сотрудника Emp1:

    using (var c = new MyContext())
    {
        var firstItem = c.Employees.Single(o => o.Node == new HierarchyId("/1/"));

        foreach (var table1 in firstItem.GetSubordinates(c))
        {
            Console.WriteLine(table1.EmployeeId + " " + table1.Name);
        }
    }

результат:

4 Emp3
7 Emp6

Ответ 2

Использование varbinary (892) вместо иерархии. EF распознает массив байтов возвращаемого байта. Вы можете преобразовать массив байтов в тип SqlHierarchyid и использовать функции hyrarchy pod. С помощью этого обходного пути вы можете использовать функции иерархии даже в других базах данных. Смотрите http://www.casavillar.com.br/blog с более подробной информацией и ссылками на самородок и github, где вы найдете образцы, включая MySql