使用EF6构建类似“Microsoft Project”的数据层次结构

时间:2014-02-05 09:50:07

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

我正在寻找使用Entity Framework 6构建一个类似“Microsoft Project”的数据层次结构。基本上我需要的是一个可以跟踪任务的不同细节级别的数据结构。所以我希望构建一个支持类似下面的结构,其中一个Task可以有子任务,子任务可以有子任务等等,但它并不总是也不总是相同的深度。

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

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

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

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对象。例如“构建游乐场A”可能具有大小为1的子任务“构建表”,而“构建游乐场B”可能具有大小为2的子构建“构建表”但我只希望“构建表”任务在数据库中存在一次然后通过TaskData对象关联数据。

非常感谢提示或帮助确定如何解决上述任何问题。

1 个答案:

答案 0 :(得分:2)

一旦你掌握了它,你的层次结构表就不是一个概念太难了。我通常工作数据库优先,而不是代码优先,所以我无法帮助你解决类定义以及其他问题,但理论如下......

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

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

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

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

然后,您的TaskData应定义自己的Id,以及您需要的任何其他内容。如果您希望TaskData可以跨多个Tasks重用,那么创建一个包含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);
    }
}