【问题标题】:Entity Framework HierarchyId Workarounds实体框架 HierarchyId 解决方法
【发布时间】:2013-01-16 16:42:37
【问题描述】:

英孚 5.0

我正在开发一个原型来一起测试hierarchyid 和实体框架。我有以下架构:

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

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

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

Employee.Subordinates

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

感谢您的帮助!

【问题讨论】:

    标签: entity-framework entity-framework-5


    【解决方案1】:

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

    【讨论】:

    • @zgabi 很好奇为什么没有进入正式版??
    • @BZ - 实体框架团队认为层次支持的需求不足以实现它,显然他们最近如何实现其他类似类型存在一些复杂性,他们希望避免和而是使用某种扩展框架来处理。但是他们没有时间进行 6 RTM。也许在未来的更新中。 :(
    • 不幸的是,其他二进制文件依赖于具有特定公钥的 EF(ASP.NET 身份),这意味着并不总是可以使用这个库。
    • @zgavi,repo 的问题在于它是一个 fork 而不是一个扩展。
    • 已合并到官方EF,所以公钥的事情会在后续版本中解决。
    【解决方案2】:

    使用 varbinary(892) 代替 hierarchyid。 EF 识别 varbinary 返回字节数组。 您可以将字节数组转换为 SqlHierarchyid 类型并使用 hyrarchy pod 函数。 使用此解决方法,您甚至可以在其他数据库中使用 hierarchyid 函数。 请参阅http://www.casavillar.com.br/blog 了解更多详细信息以及指向 nugget 和 github 的链接,您可以在其中找到包括 MySql 在内的示例

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-17
      • 1970-01-01
      相关资源
      最近更新 更多