【问题标题】:How to create a table with two foreign keys pointing to the same table using Entity Framework Code First如何使用Entity Framework Code First创建一个具有两个指向同一个表的外键的表
【发布时间】:2017-11-24 19:11:46
【问题描述】:

我有以下类,我首先尝试使用 Entity Framework 6 代码将它们存储在数据库中。

人:

public class Person
{
    public Guid Id { get; set; }

    public string Name { get; set; }

    public List<WorkItem> WorkItems { get; set; }
}

工作项:

public class WorkItem
{
    public Guid Id { get; set; }

    public string Description { get; set; }

    public Person Creator { get; set; }

}

如您所见,每个人都可以有许多任务。但是(!)该任务也有一个创建者是一个人。

我希望使用实体框架的 add-migration 创建的工作项表有两个外键。一种是使工作项可以混合到 Person 的 WorkItems 集合中,另一种是指向工作项的创建者。如下图所示:

这似乎不是一个奇怪的场景,但它给我带来了很多问题。

如果您只是尝试使用 add-migration 创建数据库表,则 WorkItem 将按以下方式创建:

            CreateTable(
            "dbo.WorkItems",
            c => new
                {
                    Id = c.Guid(nullable: false),
                    Description = c.String(),
                    Creator_Id = c.Guid(),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.People", t => t.Creator_Id)
            .Index(t => t.Creator_Id);

如您所见,这里没有任何东西使 Person 成为其工作项的所有者。如果我删除 Creator 属性,它会按预期工作。当该类是所有者时,引用该类似乎存在问题。

为什么这不能开箱即用,最好的解决方法是什么?导航属性?流畅的API?

【问题讨论】:

  • 你在哪里“引用同一张表”?您的问题和发布的代码不等价。
  • 抱歉,我发现很难给这个问题起一个好的标题。我的代码的最终结果首先应该是一个名为 WorkItem 的表,它有两个对 Person 表的引用(外键)。一个是因为 Person 有一个工作项列表,一个是因为 WorkItem 有一个人的引用。
  • 这意味着您实际上在 WorkItem 表中存储了两个 PersonId,这是您想要的吗?目前尚不清楚为什么你想要两个参考,而一个完全没问题
  • 是的,没错。 Workitem 表中应该有两个对 Person 的引用。我已经解释了原因:“正如你所看到的,每个人都可以有许多任务。但是(!)任务也有一个创造者,就是一个人。”当然,可能有更好的方法来解决这个问题。也许我的做法是错误的。

标签: c# sql-server entity-framework-6 ef-code-first


【解决方案1】:

在您的 DbContext.OnModelCreating 中使用此配置配置您的实体(或者更好的是,添加单独的实体配置):

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
   modelBuilder.Entity<Person>().HasMany(p => p.WorkItems).WithMany();
   modelBuilder.Entity<WorkItem>().HasRequired(t => t.Creator).WithMany().WillCascadeOnDelete(false);
 }

这将为 Person 创建一个表,为 WorkItem 创建另一个表,并创建另一个表以支持 Person 和 WorkItem 之间的多对多关系。它还将在 WorkItem 中创建一个单独的 FK 来引用 Person:

我无法完全理解您的问题。我知道每个人都可以有多项任务,但我不确定一项任务是只能分配给一个人还是多个人。如果您只需要一对多关系,请使用以下配置:

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
   modelBuilder.Entity<Person>().HasMany(p => p.WorkItems).WithOptional();
   modelBuilder.Entity<WorkItem>().HasRequired(t => t.Creator).WithMany().WillCascadeOnDelete(false);
 }

这只会创建两个表和 WorkItem 中的两个 FK-s 来引用 Person 表;一个用于创建者,一个用于 EF 可以将 Person 中的引用连接到 WorkItem:

【讨论】:

  • 您的第二种情况是我所追求的。我真正想了解的是为什么这不能在实体框架中开箱即用?类结构看起来很简单。
  • 您想表达两个关系,但您只指定了每个关系的一半。 EF 怎么知道这两部分不是一个整体?以下是 EF 自己知道的文档:msdn.microsoft.com/en-us/library/jj679962(v=vs.113).aspx 如果你想要更精简的版本,请看这里:dotnetfalcon.com/…
【解决方案2】:

这看起来像是一对多的关系,所以你的Tasks 属性应该是ICollection&lt;WorkItem&gt;,你需要更新你的两个类。

你的Person 类会是这样的:

public class Person
{
    public Guid Id { get; set; }

    public string Name { get; set; }

    public ICollection<WorkItem> Tasks { get; set; }
}

您还需要修改您的WorkItem 类以引用创建WorkItemPerson 实例,因此WorkItem 类将类似于:

public class WorkItem
{
    public Guid Id { get; set; }

    public string Description { get; set; }

    [ForeignKey("Person")]
    public Guid WorkItemCreatorId{ get; set; }

    public Person Creator { get; set; }

}

现在这应该会在迁移中生成正确的 sql。

有关实体框架一对多关系的更多信息,请联系a look here

希望它对你有用。

【讨论】:

  • 不幸的是,这不起作用。如果我对 Person 的任务使用 List 或 ICollection 是否重要?我认为您不能将类(“Person”)引用为 ForeignKey。
猜你喜欢
  • 1970-01-01
  • 2015-04-18
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2019-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多