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();