C# 在实体框架6代码中,两个表相互引用

C# 在实体框架6代码中,两个表相互引用,c#,entity-framework,sqlite,entity-framework-6,C#,Entity Framework,Sqlite,Entity Framework 6,我有实体Project和Sprint,其中Sprint属于一个项目。项目还包含一个积压工作,它是对默认情况下将添加项目的单个Sprint的引用 public class Project { public long ID { get; set; } public string Name { get; set; } public long BacklogId { get; set; } public Sprint Backlog { get; set; } } p

我有实体
Project
Sprint
,其中Sprint属于一个项目。项目还包含一个
积压工作
,它是对默认情况下将添加项目的单个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”和“Project”之间关联的主体端。必须使用关系fluent API或数据注释显式配置此关联的主体端

我经历了很多尝试和错误,无法通过各种问题,例如“角色中的多重性无效”问题

如何使用数据注释或
OnModelCreating()
正确地建模我所描述的这种关系。我现在有

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Sprint>()
        .HasRequired(x => x.Project)
        .WithRequiredPrincipal(x => x.Backlog);
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Entity()
.has必需(x=>x.Project)
.withrequired principal(x=>x.Backlog);
}

背景:我正在使用EF6,并使用
System.Data.Sqlite.EF6
提供程序连接到Sqlite文件,我认为您实际上需要两种不同的关系。项目和sprint(项目的所有sprint)之间有一个1:0..N,项目和sprint(项目的backlog sprint)之间有一个0..1:1,因此您需要两个model builder语句
Sprint.ProjectId
是第一个关联的FK,
Project.BacklogId
是第二个关联的FK。当然,此布局允许您指定不属于项目的积压工作,因此您需要验证它。或者,您可以引入一个
Sprint.IsBacklog
标志,在这种情况下,您只需要一个关联。

下面的
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);
}
重要提示 尽管
Project.BacklogId
可以为空,但实体框架将其视为循环引用并抛出一个

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

事实证明,EF在2012年末报告的bug/idea列表中也有同样的问题。它的当前状态是建议的,EF代表评论说:

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

-罗米勒于2013年1月25日上午9:17写信给我

变通办法 克服这一问题的方法是在事务内部两次保存到上下文中

using (var transaction = context.Database.BeginTransaction())
{
    try
    {
        var project = new Project { Name = "Project 1" };
        context.Projects.Add(project);
        context.SaveChanges();

        var backlog = new Sprint { Name = "Backlog", Project = project };
        project.Backlog = backlog;
        context.Sprints.Add(backlog);
        context.SaveChanges();

        transaction.Commit();
    }
    catch (Exception)
    {
        transaction.Rollback();
        throw;
    }
}

这适用于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();

啊,我明白了。谢谢,我来试试。我以前考虑过IsBacklog,但老实说,我认为我的模型没有那么复杂
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();