4

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

设想

三个实体:Employee, Agency, WorkRecord. 他们的目的是记录员工花在工作上的时间。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<WorkRecord> 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_EmployeeId导致循环引用。

实验

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

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

其次,我制作Employee了顶级实体,留下了它的虚拟WorkRecords集合,并Employee从实体中删除了虚拟引用属性WorkRecord......工作正常,但没有达到我的目标。

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

问题:

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

4

2 回答 2

2

听起来您只是想摆脱 EF,但我认为它实际上表达了数据耦合中的一个有效问题。例如,如果您将 AgencyId 绑定到 WorkRecord 和 Employee,那么更新 WorkRecord 上的 AgencyId 将级联到 Employee。然后将级联到 WorkRecord 等。因此是“循环引用”。您确实应该指定哪些数据对象将“拥有”与代理商的关系。

就个人而言,我怀疑最自然的绑定是从 WorkRecord 中引用代理。我可以看到员工可能从一个机构转移到另一个机构的情况,但工作记录从一个机构转移到另一个机构要困难得多。实际上,没有 WorkRecord 的 Employee 也不能真正被称为 Employee。如果您确定是这种情况,那么我会从 Employee 中删除 Agency 引用。如果您需要从员工那里到达代理机构,那么您可能无论如何都应该通过工作记录。

然而,所有这些都只是概念性的。我怀疑如果您可以让 AgencyId 在 Employee 上为空,那么 EF 将不再抱怨(并且您可能希望它在两者上都是可选的)。这应该使得在不需要使用 WorkRecord 进行循环更新的情况下更新 Employee 是有效。我必须对此进行测试才能验证,但我怀疑它会成立。

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<WorkRecord> WorkRecords { get; set; }
}
于 2012-11-06T22:55:37.080 回答
1

您可能会从 SQL Server 而不是 Entity Framework 获得异常,例如:

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

此异常基本上说明了解决问题所需要做的事情:“指定 ON DELETE NO ACTION”意味着对至少一个关系禁用级联删除。问题是所有三个关系都是必需AgencyId的,因为您的外键属性EmployeeId是不可为空的。在这种情况下,EF 将在启用删除的数据库中创建关系。当您删除 : 时,结果是多重删除路径Agency:它将删除 WorkRecords 和员工,但员工也会删除 Workrecords,因此您在 WorkRecords 上有两个多重删除路径。

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

modelBuilder.Entity<Employee>()
    .HasRequired(e => e.Agency)
    .WithMany()
    .HasForeignKey(e => e.AgencyId);

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

modelBuilder.Entity<WorkRecord>()
    .HasRequired(w => w.Employee)
    .WithMany(e => e.WorkRecords)
    .HasForeignKey(w => w.EmployeeId);

删除Agencynow会导致相关员工被删除,删除员工会导致相关工作记录被删除。但该机构不会再直接删除工作记录,因此删除了第二条删除路径。

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

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

于 2012-11-06T23:04:51.683 回答