entity framework5:代码优先的周期性关系问题

我理解为什么EF不允许PK / FK关系中的“循环引用”。 我正在寻找有关如何更改模型以使以下方案有效的建议。

脚本

三个实体: EmployeeAgencyWorkRecord 。 他们的目的是记录员工上class时间。 然后, Employee包含他/她所雇用的Agency的参考,并且他/她的工作WorkRecord包含对工作所完成的Agency的参考。

 public class Employee { [Key] public int Id { get; set; } public string Name { get; set; } public int AgencyId { get; set; } public virtual Agency Agency { get; set; } public virtual IEnumerable WorkRecords { get; set; } } public class Agency { [Key] public int Id { get; set; } } public class WorkRecord { [Key] public int Id { get; set; } public int Hours { get; set; } public int AgencyId { get; set; } public virtual Agency Agency { get; set; } public int EmployeeId { get; set; } public virtual Employee { get; set; } } 

像这样,它FK_dbo.WorkRecords_dbo.Employees_EmployeeIdFK_dbo.WorkRecords_dbo.Employees_EmployeeId导致循环引用。

实验

我的第一个想法是因为双向虚拟属性,所以我决定将两个中的一个指定为具有单向关系的顶级实体:

首先,我将WorkRecord指定为顶级实体,并从Employee实体中删除虚拟WorkRecords引用引用…生成相同的消息。

其次,我将Employee为顶级实体,保留其虚拟WorkRecords集合,并从WorkRecord实体中删除虚拟Employee引用属性……工作正常但未实现我的目标。

经过更多调查后,我发现两个实体上的代理虚拟引用属性导致循环引用。 如果一个实体删除了它,则Employee / WorkRecord实体关系可以在所有方向上工作。

题:

所以,我可以问清楚 – 如何使用WorkRecord作为我的顶级实体来表达这种商业模式,而不会让EF5感到不安?

听起来你只是想让EF退出,但我认为它实际上表达了数据耦合的有效问题。 如果将AgencyId绑定到WorkRecord和Employee,则更新WorkRecord上的AgencyId将会级联到Employee。 然后将级联到WorkRecord等。因此“循环引用”。 你真的应该指定哪些数据对象将“拥有”与Agency的关系。

就个人而言,我怀疑最自然的约束是从WorkRecord引用代理。 我可以看到一个员工可能会从一个代理商转移到另一个代理商的情况,但WorkRecord从一个代理商转移到另一个代理商则要困难得多。 还有一种情况是,没有WorkRecord的员工真的不能真正被称为员工。 如果您确定是这种情况,那么我将从员工中删除代理商参考。 如果您需要从员工那里进入代理商,那么您可能应该通过WorkRecord。

然而,所有这些仅仅是概念性的。 我怀疑,如果你使EmployId上的AgencyId成为空,那么EF不会再抱怨了(你可能希望两者都是可选的)。 这应该使得有效的员工更新,而无需使用WorkRecord 进行循环更新。 我必须测试一下来validation,但我怀疑它是否成立。

 public class Employee { [Key] public int Id { get; set; } public string Name { get; set; } public int? AgencyId { get; set; } public virtual Agency Agency { get; set; } public virtual IEnumerable WorkRecords { get; set; } } 

您可能从SQL Server而不是entity framework中获得exception,例如:

在表’XYZ’上引入FOREIGN KEY约束’ABC’可能会导致循环或多个级联路径。 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

此exception基本上说明了解决问题所需的操作:“指定ON DELETE NO ACTION”表示禁用至少一个关系的级联删除。 问题是所有三个关系都是必需的,因为您的外键属性AgencyIdEmployeeId是不可为空的。 在这种情况下,EF将使用启用删除在数据库中创建关系。 删除Agency时会导致多个删除路径:它会删除WorkRecords和Employees,但Employees也会删除Workrecords,因此WorkRecords上有两个多个删除路径。

您只能使用Fluent API禁用级联删除:

 modelBuilder.Entity() .HasRequired(e => e.Agency) .WithMany() .HasForeignKey(e => e.AgencyId); modelBuilder.Entity() .HasRequired(w => w.Agency) .WithMany() .HasForeignKey(w => w.AgencyId) .WillCascadeOnDelete(false); // or for one or more of the other relationships modelBuilder.Entity() .HasRequired(w => w.Employee) .WithMany(e => e.WorkRecords) .HasForeignKey(w => w.EmployeeId); 

现在删除Agency会导致删除相关员工,删除的员工将删除相关的工作记录。 但该机构不再直接删除工作记录,因此删除了第二个删除路径。

您也可以将其中一个关系设置为可选的 ,按惯例自动禁用级联删除(请参阅Jacob Proffitt的回答)。

顺便说一句:您不能将IEnumerable用于导航属性,您必须使用ICollection或派生的接口或实现。