【问题标题】:Building a "Microsoft Project"-like data hierarchy using EF6使用 EF6 构建类似“Microsoft Project”的数据层次结构
【发布时间】:2014-02-05 09:50:06
【问题描述】:

我希望使用 Entity Framework 6 构建一个类似于“Microsoft Project”的数据层次结构。本质上,我需要一个可以跟踪任务的不同细节级别的数据结构。因此,我希望构建一个结构来支持类似以下内容的结构,其中任务可以有子任务,子任务可以有子任务等等,但它并不总是,也不总是相同的深度。

Task
-Task
--Task
--Task
--Task
---Task
---Task
--Task

这里的许多其他帖子都提到,实体框架的树层次结构非常困难,但我无法找到任何信息来表明它是否自 EF4 以来有所改进并且现在不那么困难了。

我有一个如下所示的任务模型,但是当我尝试生成数据库时,它没有按预期生成:

public class Task
{
    public int ID { get; set; }

    public string Name { get; set; }

    public virtual Task Parent { get; set; }

    public virtual ICollection<Task> Children { get; set; }
    public virtual ICollection<Task> Predecessors { get; set; }
    public virtual ICollection<Task> Successors { get; set; }
}

我期待一个数据库,其中有一个 Tasks 表以及 Children、Predecessors 和 Successors 表,但我最终得到的只是一个具有如下属性的 Tasks 表:

ID = c.Int(nullable: false, identity: true),
Name = c.String(),
Task_ID = c.Int(),
Parent_ID = c.Int(),
Task_ID1 = c.Int(),
Task_ID2 = c.Int(),

此外,我希望能够将多个不同的数据与这些任务中的每一个相关联。所以假设我的任务是“建立一个表”,我正在跟踪 PersonA 与 PersonB 相比需要多长时间来构建它。将该数据与该任务相关联的最佳方法是什么?我应该使用如下的 TaskData 对象吗:

public class TaskData
{
    public int ID { get; set; }

    public int CompletionTime { get; set; }

    public virtual Task Task { get; set; }
    public virtual Person Person { get; set; }
}

为了使事情更加复杂,我还需要使同一个 Task 对象可跨多个不同的 Task 重用。例如“Build Playground A”可能有大小为 1 的子任务“Build a Table”,而“Build Playground B”可能有大小为 2 的子任务“Build a Table”,但我只希望“Build a Table”任务在 DB 中存在一次然后通过TaskData对象关联数据。

我们将非常感谢您在确定如何解决上述任何问题时提供提示或帮助。

【问题讨论】:

    标签: c# entity-framework asp.net-web-api entity-framework-6


    【解决方案1】:

    一旦您掌握了层次结构表的概念,它就不会太难了。我通常使用数据库优先而不是代码优先,所以我无法为您提供类定义以及其他方面的帮助,但理论如下......

    您的任务应该具有以下列:

    • Id(这是您的主键(例如))
    • ParentId(这是与 Id 类型相同的可空列)
    • 您需要的其他任何东西

    然后,您应该定义一个关系,其中表在主键侧使用 Id 引用自身,在外键侧使用 ParentId。

    您不应该拥有单独的表格。所有任务都将包含在一个表中。

    然后,您的 TaskData 应定义其自己的 Id,以及您需要的任何其他内容。如果您希望 TaskData 可跨多个 Task 重用,则创建一个包含 TaskDataId 和 TaskId 列的多对多表,用它们创建一个复合键,并通过该表链接 Tasks 和 TaskData。任何特定于 Tasks 和 TaskData 之间关系的数据都可以在 Link 表中定义。

    您必须小心层次结构的地方是,当您请求大量数据时,查询会变得很慢。每次向下循环层次结构时,EF 都会生成一个单独的查询。我强烈建议您检索实现使用公用表表达式 (CTE) 的存储过程并在 EF 中映射该存储过程的完整层次结构。

    编辑 - 帮助任务类(如果不完全正确,请原谅我,但它应该为您指明正确的方向)。

    1 - 创建你的任务

    public class Task
    {
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int TaskId { get; private set; }
    public string Name { get; set; }
    public int? ParentTaskId { get; private set; }
    
    [ForeignKey("ParentTaskId")]
    public Task ParentTask { get; set; }
    
    public List<Task> SubTasks { get; set; }
    
    public Task()
    {
    SubTasks = new List<Task>();
    }
    }
    

    2 - 将 DbSet 自动属性添加到您的 DbContext 子类

    3 - 覆盖 DbContext 类中的 OnModelCreated 方法以配置关联。

    public class YourContext : DbContext
    {
        public DbSet<Task> Tasks { get; set; }
    
        // Other stuff
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Task>()
                .HasMany(t => t.SubTasks)
                .WithOptional(t => t.ParentTask);
        }
    }
    

    【讨论】:

    • 我同意应该只有一个任务表,我只是想其他人会跟踪任务之间的关系。在您解释的内容中,我唯一遇到的问题是如何跟踪 id 并让 EF 生成导航表。
    • @bbodenmiller 原谅我,我不完全确定你的意思。当您说跟踪 ID 时,您指的是您提供的类中提到的“Children”、“Predecessors”和“Successors”属性吗?
    • @bbodenmiller 为了今晚帮助你,我假设你的意思是我上面所说的。了解如何在第 3 步中定义双向关联。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    相关资源
    最近更新 更多