这是实体框架或bug的预期行为吗?

时间:2016-02-26 12:39:39

标签: entity-framework entity-framework-6

我正在使用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中看到的

enter image description here

4 个答案:

答案 0 :(得分:2)

以下是发生的事情:

dbContext.Users.Add(new User { Email = "x@b.com", Name = "x" });

已创建UserId = 0对象。

dbContext.Vehicles.Add(new Vehicle { BuildYear = 2016, Model = "BMW X4" });

已创建VehicleUserId = 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相同