为什么实体框架假定该外键的值

本文关键字:实体 框架 为什么 | 更新日期: 2023-09-27 18:26:50

我有以下模型。

public class Parent
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
    public virtual Parent Parent { get; set; }
}

在下面的代码中,EF将父对象和子对象都插入到数据库中,其中dbo.Child.ParentId字段引用dbo.Parent.Id,即使从未设置child.ParentId属性。

var parent = new Parent { Name = "p_1" }; 
var child = new Child { Name = "ch_1" };       // ParentId == 0
var con = new MyContext();
con.Set<Child>().Add(child);
con.Set<Parent>().Add(parent);
con.SaveChanges();  // saved successfully 

但在下面的代码中,EF无法保存引发此异常的更改:

无法确定"TestEfProxy.Child_Parent"关系的主体端。多个添加的实体可能具有相同的主键。

代码:

var parent = new Parent { Name = "p_1" }; 
var parent2 = new Parent { Name = "p_2" };
var child = new Child { Name = "ch_1" };
var con = new MyContext();
con.Set<Parent>().Add(parent);
con.Set<Child>().Add(child);
con.Set<Parent>().Add(parent2);
con.SaveChanges();               // throws exception

为什么EF在第一种情况下决定child.ParentId引用parent.Id?为什么在第二种情况下它不这样做?这个错误消息的含义是什么?

为什么实体框架假定该外键的值

设置子的父级

var child = new Child { Name = "ch_1", Parent = parent }

上下文中有两个Id为0的父级,而子级没有应该附加到的引用。在实例化新实例时,不可为null的类型Int将获得默认值0。

根据文档实体框架代码优先约定:

主键约定

如果类上的属性名为"ID"(不区分大小写),或者类名后跟"ID"。如果主键属性的类型是数字或GUID,则它将被配置为标识列。

外键约定

任何数据类型与主体主键属性相同且名称遵循以下格式之一的属性都表示关系的外键:"、"或"。如果找到多个匹配项,则按上面列出的顺序给予优先级。外键检测不区分大小写。当检测到外键属性时,Code First会根据外键的可空性推断关系的多重性。如果属性可以为null,则将关系注册为可选关系;否则根据需要注册关系。