EntityFramework 在创建时自动分配导航属性吗?

6
当我运行下面所述的代码时,EF会将PersonAddress与正确的PersonId保存。虽然我没有将PersonAddress实体添加到Person实体中,但我的数据库记录已经正确关联。
我的问题是:即使我没有指定它所属的实体,EF是否自动添加相关实体?如果是,这不会导致不必要的实体关系吗?
更新
出于以下原因,似乎实体已被正确保存:
  1. 创建实体Person时,PersonId字段为0
  2. PersonAddress.PersonId在创建时也为0。
通过在创建时手动设置Person.PersonId为任何值,然后将PersonAddress.PersonId设置为相同值的Person.PersonId,EF将数据保存正确,因为它们共享相同的PersonId
因此,从技术上讲,EF并未自动添加相关实体,它们是相关的,因为它们共享相同的PersonId
请参考以下代码:
        using (var context = new Models.TestEntities())
        {
            var person = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          = context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.PersonAddresses.Add(postalAddress);
            context.PersonAddresses.Add(residentialAddress);

            context.SaveChanges();
        }

当我把额外的Person添加到代码中时,我遇到了以下错误:enter image description here
代码:
        using (var context = new Models.TestEntities())
        {
            var person  = context.People.Create();
            var person2 = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          = context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.People.Add(person2);                

            context.PersonAddresses.Add(postalAddress);
            context.PersonAddresses.Add(residentialAddress);

            context.SaveChanges();
        }

作为错误指定的,Entity Framework 现在无法确定 PersonAddress 实体属于哪个人。
我可以通过以下修改代码来解决这个问题:
        using (var context = new Models.TestEntities())
        {
            var person  = context.People.Create();
            var person2 = context.People.Create();

            var postalAddress           = context.PersonAddresses.Create();
            postalAddress.AddressLine1  = "PostalAddressLine1";

            var residentialAddress          =    context.PersonAddresses.Create();
            residentialAddress.AddressLine1 = "ResidentialAddressLine1";

            context.People.Add(person);
            context.People.Add(person2);

            person.PersonAddresses.Add(residentialAddress);
            person.PersonAddresses.Add(postalAddress);

            context.SaveChanges();
        }

请看下面的EDMX:

enter image description here

请看用于创建这两个表的SQL脚本:
CREATE TABLE Person
(
   PersonId INT IDENTITY(1,1) NOT NULL CONSTRAINT [PK_Person] PRIMARY KEY,
   FirstName VARCHAR(250)
)

CREATE TABLE PersonAddress
(
   PersonAddressId INT IDENTITY(1,1) NOT NULL CONSTRAINT [PK_PersonAddress]     PRIMARY KEY,
  PersonId INT NOT NULL,
  AddressLine1 VARCHAR(250)
)

  ALTER TABLE PersonAddress
  ADD CONSTRAINT [FK_PersonAddress_Person] FOREIGN KEY(PersonId)
  REFERENCES [Person](PersonId)

请查看下面的Id列截图: enter image description here enter image description here enter image description here 请看下面的SQL Server Profiler跟踪: :
enter image description here
enter image description here
enter image description here 请查看在SQL中插入的记录: :
enter image description here
enter image description here 谢谢。

1
我没有看到第一段和第二段代码之间有任何好的变化,可以解决代码中的错误(DbUpdateException)。 - Jenish Rabadiya
我可以发布数据库模型的图片吗?因为这是唯一的两个表。 - Tjaart van der Walt
1
Jenish Rabadiya - 请注意,在最后一个代码片段中,我调用了person.PersonAddresses.Add而不是context.PersonAddresses.Add( - Tjaart van der Walt
是的,PersonAddress Id 是自增的,请查看更新后附带的 SQL 脚本以创建这两个表。 - Tjaart van der Walt
检查 EF 是否配置为使用这些身份标识值。StoregeneratedPattern 应正确配置。 - usr
显示剩余6条评论
4个回答

4

以下是发生的情况:

var person = context.People.Create();

创建一个 Person,其 PersonId 值为 0。
var postalAddress           = context.PersonAddresses.Create();
postalAddress.AddressLine1  = "PostalAddressLine1";
var residentialAddress          = context.PersonAddresses.Create();
residentialAddress.AddressLine1 = "ResidentialAddressLine1";

地址被创建,同时具有 PersonId = 0。

context.People.Add(person);
context.PersonAddresses.Add(postalAddress);
context.PersonAddresses.Add(residentialAddress);

EF已经执行了关系修复,即它匹配了地址的PersonIdPersonPersonId(都为0),并建立了它们之间的关联。

context.SaveChanges();

数据库已经为 Person.PersonId 分配了一个身份值。EF 从数据库中读取它并将其赋给实体。

在第二个片段中有两个 Person,它们的 PersonId 值都为0,所以 EF 不知道应该将地址与哪个人相关联。

应该怎么做

显然,这是意外行为。如果实体之间有关联,则最好明确地将它们关联起来,就像第三个片段中那样。

一旦您了解了这些自动关联,您可能希望通过为 Person.PersonId 分配一个不同于 0 的默认值(例如 -1)来防止它们。现在,EF 将不会将此 id 与任何其他整数的默认值(0)匹配的外键值相匹配。


1
将其设置为-1会抛出错误插入冲突,这意味着它有效。非常好的答案! - Jenish Rabadiya
一个总是让人感到有趣的问题的答案(刚刚让我浪费了几个小时):“那怎么可能会起作用?” - Mark Peters

1
我已经通过使用您的脚本创建新数据库,并针对这个新数据库创建新项目来测试了该场景。然后我生成一个模型并保留所有默认设置。
以下是我用于创建人员和地址的代码。
        var entities = new TestEntities();

        var person = entities.People.Create();
        person.FirstName = "xxx";
        entities.People.Add(person);

        var address1 = entities.PersonAddresses.Create();
        address1.AddressLine1 = "Line1";
        entities.PersonAddresses.Add(address1);

        var address2 = entities.PersonAddresses.Create();
        address2.AddressLine1 = "Line1";
        entities.PersonAddresses.Add(address2);

        entities.SaveChanges();

我可以确认这段代码确实能够运行并将一个人和两个与该人相关联的地址插入。如果只有一个实体可能需要连接,EF会自动连接它。我不确定它是否应该是一个特性,但这可能会导致一些不好的意外情况。

我同意,这可能会导致创建不必要的关联。 - Tjaart van der Walt
有趣的是,我在地址(Address)上尝试了一个可空(PersonId)的人员ID。在这种情况下,人员ID仍然保持为空(null)。这让我更加怀疑这应该是一项功能(feature)。 - Johann Strydom
这并没有回答问题。 - Jenish Rabadiya
@JenishRabadiya 的问题是“即使我没有指定它所属的实体,Enityframework是否会自动添加相关实体?” 我回答说:“如果只有一个实体可能与之链接,EF肯定会将其连接起来。” 我不确定为什么你认为这并没有回答问题。 如果问题是为什么EF这样做,那就是另一回事了。 - Johann Strydom
如果这是问题,那么在OP的帖子中已经有了答案。第一个片段运行良好。 - Jenish Rabadiya

0

在这里,您将两个人实体附加到上下文

context.People.Add(person);
context.People.Add(person2);

然后,您将这两个地址附加到第一个人实体

person.PersonAddresses.Add(residentialAddress);
person.PersonAddresses.Add(postalAddress);

基本上,你告诉实体框架你想把这两个地址映射到第一个人身上。如果你不这样做,实体框架就不会自动映射它们,除非你手动在地址中输入 personID。

在你的第一段代码中,你只创建了人和地址,并试图将它们添加到数据库中。它们之间没有建立映射关系,实体框架也不知道它们是否应该链接起来。


第一段代码确实有效,EF会正确地将它们链接起来。请运行第一段代码,您将看到结果。 - Tjaart van der Walt

0

EF 无法自动添加相关实体,如果您没有指定第二个实体,原因是数据库中的数据完整性约束,希望这可以帮到您。


如果 EF 不会自动执行这个操作,那么为什么我的第一段代码片段可以工作呢? - Tjaart van der Walt
我看它似乎不起作用,你尝试插入一个PersonAddresses对象了吗? - Hamzeh Wehbe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接