不使用导航属性添加相关实体

本文关键字:实体 添加 属性 导航 | 更新日期: 2023-09-27 18:07:21

我有以下类,设置用于测试:

public class Company
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
}
public class Employee
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public int CompanyId { get; set; }
    public virtual Company Company { get; set; }
}
public class EFTestDbContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Company> Companies { get; set; }
}

为了进行测试,我想用单个SaveChanges调用插入一个公司和该公司的一名员工,如下所示:

Company company = new Company
{
    Name = "Sample company"
};
context.Companies.Add(company);
// ** UNCOMMENTED FOR TEST 2
//Company company2 = new Company
//{
//    Name = "Some other company"
//};
//context.Companies.Add(company2);
Employee employee = new Employee
{
    Name = "Hans",
    CompanyId = company.Id
};
context.Employees.Add(employee);
context.SaveChanges();

尽管我不使用导航属性,但我已经在Id上建立了关系,这不知何故神秘地工作了-员工被保存了适当的外键到公司,从0更新到实际值,这让我去?!?!一些隐藏的c#特性?

然后我决定添加更多的代码,在上面的代码片段中进行了注释,使其插入2 x Company实体和1 x Employee实体,然后我得到了exception:

无法确定CodeLab.EFTest的主端。Employee_Company"关系。多个添加的实体可以有相同的主键。

这是否意味着在外键为0的情况下,并且在相同的SaveChanges事务中插入了单个匹配实体,实体框架将假定外键应为该匹配实体?

在第二个测试中,当有两个实体匹配关系类型时,Entity Framework抛出异常,因为它无法确定应该与哪一个Companies Employee相关。

编辑:

我又做了一个测试,注释掉了一行。第一个测试仍然正常运行(因为int的默认值是0):

Employee employee = new Employee
{
    Name = "Hans",
    //CompanyId = company.Id // * no need for this at all
};

不使用导航属性添加相关实体

你并没有真正触及一个隐藏的c#特性,也许是一个模糊的实体框架特性。

当你分配CompanyId时,EF知道拥有Id = 0的公司(当时)是该员工的母公司。在许多情况下,EF执行DetectChanges,它也执行关系修复:匹配外键值和引用。当执行

时就会发生这种情况
context.Employees.Add(employee);

现在EF将使用引用,而不是外键值,因此它知道要在数据库中存储哪个FK值。

当您创建两个公司时,有两个实例具有相同的瞬态键值,因此EF不能再选择。

所以当你想要存储新的连接对象时,总是建议设置引用而不是FK值。

您已将公司添加到companies表中。

context.Companies.Add(company);

然后设置员工的companyId:

CompanyId = company.Id

这是在sql中创建关系所需的全部内容。可能是实体框架插入了您的公司,然后插入了具有最后生成的标识的companyId的员工。

看这里,我们可以看到当存在重复实体时使用id存在问题。在你的例子中,两个公司的id都是0,直到它们被保存,所以实体框架不能唯一地标识一个公司。