我有实体Project和Sprint,其中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‘和'Project’之间关联的主端。必须使用关系fluent 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文件
发布于 2014-11-24 09:23:58
下面的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将积压设置为可选,并使用long?使Project.BacklogId为空是很重要的。否则会有一个循环引用,我们将无法创建任何一个实体。保留项目上的待定引用(因为它属于项目)是有意义的,而不是在Sprint上创建IsBacklog。
对Project的Sprint集合的附加引用-感谢https://stackoverflow.com/a/27088107/80428和https://stackoverflow.com/a/27090274/80428指出这一点。
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; }
}重要注记
尽管Project.BacklogId是可空的,实体框架将其视为循环引用,并抛出一个DbUpdateException
无法确定从属操作的有效排序。依赖项可能由于外键约束、模型需求或存储生成的值而存在。
事实证明,2012年末,EF的bug/idea列表中出现了票#142,其问题与此完全相同。它的现状是提出的,并有一名EF代表评论说:
我们同意,这将是一个很好的设想。考虑到我们在EF6发行版中的位置,以及这个特性的大小和影响,我们的团队没有计划在EF6中实现它。因此,我们正在将其转移到未来版本,以便在下一个版本中重新考虑。 - RoMiller于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;
}
}发布于 2014-11-23 10:49:27
我觉得你真的需要两种截然不同的关系。在项目和sprint之间有一个1:0..N (项目的所有sprint ),在项目和sprint之间有0.1:1(项目的待办冲刺),所以您需要两个模型生成器语句。Sprint.ProjectId是第一个协会的FK,Project.BacklogId是第二个。当然,这个布局允许您指定一个不属于项目的待办事项,所以需要对其进行验证。或者,您可以引入一个Sprint.IsBacklog标志,在这种情况下,您只需要一个关联。
发布于 2019-06-18 06:59:40
这适用于EF核心。
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();https://stackoverflow.com/questions/27088054
复制相似问题