两个表在Entity Framework 6 Code First中相互引用

时间:2014-11-23 10:42:53

标签: c# entity-framework sqlite entity-framework-6

我有实体ProjectSprint,其中Sprint属于项目。项目还包含Backlog,它是对默认情况下将添加项目的单个Sprint的引用。

public class Project
{
    public long ID { get; set; }
    public string Name { get; set; }

    public long BacklogId { get; set; }
    public Sprint Backlog { get; set; }
}

public class Sprint
{
    public long ID { get; set; }
    public string Name { get; set; }

    public long ProjectId { get; set; }
    public Project Project { get; set; }
}

实体框架显然无法从上面确定这两个实体之间的关系并抛出

  

其他信息:无法确定类型' Sprint'之间关联的主要目标。和'项目'。必须使用关系流畅API或数据注释显式配置此关联的主要结尾。

我经常试错,无法通过各种问题,例如'多重性在角色中无效。的问题。

如何使用数据注释或OnModelCreating()正确描述这种关系。我目前有

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Sprint>()
        .HasRequired(x => x.Project)
        .WithRequiredPrincipal(x => x.Backlog);
}

背景:我使用EF6并使用System.Data.SQLite.EF6提供程序连接到Sqlite文件

3 个答案:

答案 0 :(得分:2)

我认为你实际上需要两种不同的关系。项目和sprint之间有1:0..N(项目的所有冲刺),项目和sprint之间的0..1:1(项目的积压冲刺),所以你需要两个模型构建器声明。 Sprint.ProjectId是第一个关联的FK,Project.BacklogId是第二个关联。当然,这种布局允许您指定不属于项目的待办事项,因此您需要对其进行验证。或者,您可以引入Sprint.IsBacklog标志,在这种情况下,您只需要一个关联。

答案 1 :(得分:2)

以下OnModelCreating()有效并且看似正确。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Sprint>()
        .HasKey(s => s.ID)
        .HasRequired(s => s.Project)
        .WithMany(p => p.Sprints)
        .HasForeignKey(s => s.ProjectId);

    modelBuilder.Entity<Project>()
        .HasKey(p => p.ID)
        .HasOptional(p => p.Backlog);
}

使用HasOptional使用modelBuilder并使用Project.BacklogId使long?可空,将Backlog设置为可选项非常重要。否则有循环引用,我们将无法创建任何实体。将Backlog引用保留在Project(因为它属于Project)而不是在Sprint上创建IsBacklog是有意义的。

额外引用Project的{​​{1}} 集合 - 感谢@fejesjoco@Zakos指出

Sprint

重要提示

即使public class Project { public long ID { get; set; } public string Name { get; set; } public ICollection<Sprint> Sprints { get; set; } // new public long? BacklogId { get; set; } // changed public Sprint Backlog { get; set; } } public class Sprint { public long ID { get; set; } public string Name { get; set; } public long ProjectId { get; set; } public Project Project { get; set; } } 可以为空,实体框架也会将此视为循环引用并抛出 DbUpdateException

  

无法确定相关操作的有效排序。由于外键约束,模型要求或存储生成的值,可能存在依赖关系。

事实证明,2012年底报告的EF错误/想法列表上有ticket #142,但问题完全相同。目前的状态已经提出,EF代表发表评论说:

  

我们同意这是一个很好的启用方案。考虑到我们在EF6版本中的位置以及此功能的大小和影响,我们的团队不打算在EF6中实现它。因此,我们正在将其转移到Future版本,以便在下一版本中重新考虑。

     

- RoMiller于2013年1月25日上午9:17写道

解决方法

解决这个问题的方法是在事务中保存两次上下文

Project.BacklogId

答案 2 :(得分:0)

这可用于EF Core。

public class Project
{
    public long ID { get; set; }
    public string Name { get; set; }

    // explicit constraint is required to resolve child-dependent relationship
    [ForeignKey("BacklogId")]
    public virtual Sprint Backlog { get; set; }
}

public class Sprint
{
    public long ID { get; set; }
    public string Name { get; set; }

    // explicit constraint is required to resolve child-dependent relationship
    [ForeignKey("ProjectId")]
    public virtual Project Project { get; set; }
}

var project = new Project { Name = "Some project" };

// intermediate saving is required to avoid circular dependency exception
context.Projects.Add(project);
context.SaveChanges();

var backlog = new Sprint { Name = "Backlog sprint", Project = project };
project.Backlog = backlog;

context.Sprints.Add(backlog);
context.SaveChanges();