我正在使用Entity框架版本6.1.3并发现了一个意外的行为,因此我创建了一个可以重现它的小代码
当我执行下面的代码,并在数据库中看到车辆的用户ID设置为先前插入的用户。它应抛出与参照完整性相关的异常
class Program
{
private static void Main(string[] args)
{
using (MyDbContext dbContext = new MyDbContext())
{
dbContext.Users.Add(new User { Email = "x@b.com", Name = "x" });
dbContext.Vehicles.Add(new Vehicle { BuildYear = 2016, Model = "BMW X4" });
dbContext.SaveChanges();
}
}
}
class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public virtual ICollection<Vehicle> Vehicles { get; set; }
}
class Vehicle
{
public int Id { get; set; }
public int UserId { get; set; }
public string Model { get; set; }
public int BuildYear { get; set; }
public virtual User User { get; set; }
}
class MyDbContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
}
修改
我正在使用代码第一种方法。当我在数据库中看到结果时,车辆表中的userid是最后插入的用户ID。 数据库AFAIK中没有触发器
这就是我在SSMS中看到的
答案 0 :(得分:2)
以下是发生的事情:
dbContext.Users.Add(new User { Email = "x@b.com", Name = "x" });
已创建User
个Id = 0
对象。
dbContext.Vehicles.Add(new Vehicle { BuildYear = 2016, Model = "BMW X4" });
已创建Vehicle
,UserId = 0
。
由于键值相同,EF会将两个对象视为已连接。
当SaveChanges
运行时,EF执行 relationship fixup ,通过它填充Vehicle.User
并将Vehicle.UserId
设置为新用户的Id值,它是从数据库中。
您正确地期望违反参照约束,因为UserId
不能是null
而您似乎没有设置它。解决此问题的唯一方法是设置Vehicle
的{{1}}属性。您无法设置User
,因为Vehicle.UserId
的ID尚未知晓:
User
(顺便说一下,我被以前插入的用户这个短语所左右跟踪,建议EF在运行代码之前连接你插入的用户。)
答案 1 :(得分:1)
您没有填写UserId
属性,因此该属性默认值为0
。您的User
(我认为它是您插入的第一个用户,因为它的测试代码)也会从数据库中获得Id = 0
。因此,当您同时保存它们时,Vehicle
将与您的第一个User
建立外键关系。
顺便说一句,你所做的事情是不正确的。如果您的Vehicle
导航属性可以为null,则还应创建相应的外键Nullable
。否则,您必须为您创建的每个新User
分配一个Vehicle
。
答案 2 :(得分:1)
我无法在任何地方找到它,但显然是因为车辆和用户之间存在必需的关系,并且您在一个批处理中添加两者,实体框架决定它们必须属于一起,而不是执行指定的查询并让数据库返回引用约束错误。
我不能说我喜欢这个。
当您评论希望此关系是可选的时,解决方案是制作UserId
类型的int?
。然后不会发生所描述的行为。
答案 3 :(得分:1)
这是一种EF行为。
Vehicle.Id和User.Id生成为自动编号字段 Vehicle.UserId是默认情况下与Vehicle.User关系的id关联的字段。
对于您的课程,如果您没有设置Vehicle.User,您还必须在与该车辆相关联的上下文中添加一个且仅一个用户,否则您将收到异常。
如果将UserId更改为可选,则行为会按预期更改,并且用户不会自动关联,但它将始终与Vehicle.User.Id相同