实体框架层次结构 [英] Entity Framework HierarchyId Workarounds

查看:114
本文介绍了实体框架层次结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

EF 5.0



我正在研究一个原型,以一起测试层次结构和实体框架。我有以下模式:

 创建表dbo.Employee 

EmployeeId int identity not null,
名称nvarchar(100)not null,
节点hierarchyid not null,
NodePath作为Node.ToString()持久化,
级别AS Node.GetLevel()持久化,
ManagerNode作为Node.GetAncestor(1)持久化,
ManagerNodePath作为Node.GetAncestor(1).ToString()持久化
);

更改表dbo.Employee
添加约束EmployeePK主键NonClustered(EmployeeId);

Go

- Enforce Hierarchy
更改表dbo.Employee
添加约束EmployeeManagerNodeNodeFK外键(ManagerNode)参考Employee(Node);
Go

在dbo.Employee(Node)上创建唯一的聚簇索引EmployeeDepthFirstIndex;

Go

在dbo.Employee(Level,Node)上创建NonClustered Index EmployeeBreathFirstIndex;

Go

从我的阅读中,目前不支持hierarchyid数据类型在EF中,但有些已经建议了解决方法,例如创建计算列(Node.ToString()),我已经在上面做了。



有没有办法设置EF,以便识别父/子关系,所以我可以有效地拥有下属集合?例如

  Employee.Subordinates 

我唯一可以想到的是创建一个具有FK的ManagerId列,但是我有效地将层次结构存储在两个位置。



感谢任何帮助!

解决方案

EF6现在是开源,所以很容易添加HierarcyID支持。我也添加了它。
您可以从codeplex下载修改后的源代码和已签名的dll:
http ://entityframework.codeplex.com/SourceControl/network/forks/zgabi/efhierarchyidrc1 (有时叉名更改)
或从NuGet: https://www.nuget.org/packages/EntityFrameworkWithHierarchyId/
当前的EF6处于RC1状态,但我会将修改合并到每个后续版本的EF6



我有以下模型:

  public class Employee 
{
public int EmployeeId {get;组; }
[必需,最大长度(100)]
public string Name {get;组; }
[必需]
public HierarchyId Node {get;组; }

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]。 [员工]([节点]));
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>员工{get;组;
}

生成的数据库:




[EmployeeId] [int] IDENTITY(1,1)NOT NULL,
[名称] [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])
参考[dbo]。[Employees]([Node])

Exa mple获取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


EF 5.0

I am working on a prototype to test hierarchyid and entity framework together. I have the following schema:

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

From my reading, the hierarchyid datatype isn't currently supported in EF, but some have suggested workarounds such as creating calculated columns (Node.ToString()) which I have done above.

Is there a way to setup EF so that it recognizes the Parent/Child relationship so I can effectively have a subordinates collection? e.g.

Employee.Subordinates

The only thing I can think of is to create a ManagerId column w/ a FK, but then I am effectively storing the hierarchy in two places.

Thanks for any help!

解决方案

EF6 is now open source, so it is easy to add HierarcyID support. I have added it, too. You can download the modifed source and the complied/signed dlls from codeplex: http://entityframework.codeplex.com/SourceControl/network/forks/zgabi/efhierarchyidrc1 (sometimes the fork name changes) Or from NuGet: https://www.nuget.org/packages/EntityFrameworkWithHierarchyId/ Currenty EF6 is in RC1 state, but I'll merge the modifications to every later releases of EF6.

I have the following model:

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; }
}

Generated database:

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])

Example to get the child nodes of Emp1 employee:

    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);
        }
    }

result:

4 Emp3
7 Emp6

这篇关于实体框架层次结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆