Arjun Vachhani
Arjun Vachhani

Reputation: 1958

Is this expected behaviour of entity framework or bug?

i was working with Entity framework version 6.1.3 and found an unexpected behaviour, so i have created a small code that can reproduce it

when i execute below code, and see in database vehicle's userid is set to previously inserted user. it should throw exception related to referential integrity

class Program
{
    private static void Main(string[] args)
    {
        using (MyDbContext dbContext = new MyDbContext())
        {
            dbContext.Users.Add(new User { Email = "[email protected]", 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; }
}

EDIT

i am using code first approach. when i see result in database, userid in vehicle table is last inserted user id. there is no triggers in database AFAIK

this is what i am seeing in SSMS

enter image description here

Upvotes: 2

Views: 110

Answers (4)

Gert Arnold
Gert Arnold

Reputation: 109080

Here's what happens:

dbContext.Users.Add(new User { Email = "[email protected]", Name = "x" });

A User object has been created having Id = 0.

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

A Vehicle has been created, having UserId = 0.

Because of the identical key values, EF considers both objects as connected.

When SaveChanges runs, EF executes relationship fixup by which it populates Vehicle.User and sets Vehicle.UserId to the new user's Id value, which it obtained from the database.

You correctly expected a referential constraint to be violated, as UserId can't be null and you don't seem to set it. The only way you can fix this is by setting the Vehicle's User property. You can't set Vehicle.UserId because the User's Id isn't known yet:

var user = new User { Email = "[email protected]", Name = "x" };
dbContext.Users.Add(user);
dbContext.Vehicles.Add(new Vehicle { BuildYear = 2016, Model = "BMW X4", User = user });

(By the way, I was side-tracked by the phrase previously inserted user, which suggested EF connected a user you inserted before running your code).

Upvotes: 2

bubi
bubi

Reputation: 6491

It's an EF behaviour.

Vehicle.Id and User.Id are generated as autonumbering fields.
Vehicle.UserId is the field that is associated by default to the id of the Vehicle.User relationship.

With your classes, if you don't set Vehicle.User you also must have added one and only one User in the context that will be associated with the vehicle otherwise you will receive an exception.

If you change the UserId to optional the behaviour changes as expected and the user is not automatically associated but it will be Always the same as Vehicle.User.Id

Upvotes: 1

CodeCaster
CodeCaster

Reputation: 151588

I cannot find it documented anywhere, but apparently because there's a required relationship between vehicle and user, and you add both in one batch, Entity Framework decides that they must belong together, instead of executing the specified queries and letting the database return a referential constraint error.

I can't say I like this.

The solution, as you commented that you want this relation to be optional, is to make the UserId of type int?. Then the described behavior will not occur.

Upvotes: 1

Alexander Derck
Alexander Derck

Reputation: 14488

You're not filling in the UserId property, so that property will be 0 by default value. Your User (I assume it's the first user you insert as it's testcode) will get Id = 0 from the database too. So when you save them both, Vehicle will have a foreign key relationship with your first User.

What you're doing is not correct by the way. If your Vehicle navigation property can be null, you should make the corresponding foreign key Nullable too. Otherwise you have to assign a User to every new Vehicle you create.

Upvotes: 1

Related Questions