jsgoupil
jsgoupil

Reputation: 3997

Two different objects with same key for entity framework does not work

I am trying to insert object reference in my main object but EntityFramework will complain if I don't use its previously managed object. I simply want to avoid having a dependency on the dbContext when creating my object.

Simplified example:

class Movie {
    public ApplicationUser Owner { get;set; }
}

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };

// I have to attach or change its state, otherwise, EF will complain the object is not complete
db.Entry(myMovie.Owner).State = EntityState.Unchanged;

Somehow, if the same ApplicationUser has been previously loaded by the context, I get this error:

Saving or accepting changes failed because more than one entity of type 'ApplicationUser' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration.

How can I avoid this problem? Optimally, I would like to not have to tell the state of that new object.

Upvotes: 12

Views: 9785

Answers (2)

Matt Sanders
Matt Sanders

Reputation: 983

If you have an instance where you are only reading data and not modifying it you can use AsNoTracking() this will prevent having an attached instance of the model that your context knows about (it is essentially read only).

The following code should work even though it has retreived the same object twice.

var myMovieReadOnly = db.Movies.AsNoTracking().FirstOrDefault(m, m => m.Id = 1);

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.Owner = new ApplicationUser { Id = 2 };

db.SaveChanges();

Another note is that AsNoTraking() can also save on performance in scenarios where you are only reading data.


Edit: Just reread and realized it is the ApplicationUser model and not the movie, but the same concept should apply with retreival of the first instance.


Edit2:

From our comments you could also do the following the prevent needing to do the lookup at all if you already know the ID:

class Movie {
    public int OwnerId { get;set; }
    public ApplicationUser Owner { get;set; }
}

var myMovie = db.Movies.FirstOrDefault(m, m => m.Id = 1);
myMovie.OwnerId = 2;
db.SaveChanges();

Upvotes: 6

Gert Arnold
Gert Arnold

Reputation: 109119

if the same ApplicationUser has been previously loaded by the context, I get this error

So first check if the ApplicationUser is loaded.

var newId = 2;
var newApplicationUser = db.ApplicationUsers.Local.FirstOrdefault(u => u.Id == newId)
                             ?? new ApplicationUser { Id = newId };
myMovie.Owner = newApplicationUser;

Upvotes: 2

Related Questions