【问题标题】:Adding Related Entities without using navigation properties在不使用导航属性的情况下添加相关实体
【发布时间】:2014-06-06 20:49:34
【问题描述】:

我有以下课程,用于测试:

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# 功能?

然后我决定添加更多代码,在上面的 sn-p 中进行了注释,使其插入 2 x Company 实体和 1 x Employee 实体,然后出现异常:

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

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

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

编辑:

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

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

【问题讨论】:

  • 您的最后一次编辑更好地证明了明确设置引用是绝对必要的。这可能会导致意外行为。甚至可以称其为错误。

标签: c# sql entity-framework


【解决方案1】:

您并没有真正触及隐藏的 C# 功能,也许是一个不起眼的实体框架功能。

当您分配CompanyId 时,EF 知道拥有Id = 0(当时)的公司是员工的父公司。在许多情况下,EF executes DetectChanges,它也执行relationship fixup:匹配外键值和引用。执行时会发生这种情况

context.Employees.Add(employee);

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

当你创建两个公司时,有两个实例具有相同的瞬态键值,所以 EF 不能再选择了。

因此,当您想要存储新的连接对象时,始终建议设置引用而不是 FK 值。

【讨论】:

    【解决方案2】:

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

    context.Companies.Add(company);
    

    然后你设置员工的 companyId:

    CompanyId = company.Id
    

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

    查看here,我们可以看到在存在重复实体时使用 id 存在问题。在您的情况下,两家公司的 id 均为 0,直到它们被保存,因此实​​体框架无法唯一标识一家公司。

    【讨论】:

      猜你喜欢
      • 2011-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多