Elias Garcia
Elias Garcia

Reputation: 7282

Initialize data with Entity Framework

I want to initialize dummy data to the database before the application runs. I have a SQL script which inserts data directly to the tables.

The problem is when I want to update an element I get this exception:

'System.InvalidOperationException' en EntityFramework.dll: Attaching an entity of type 'Es.Udc.DotNet.MiniPortal.Model.Book' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

This is my update method:

public void Update(E entity)
{
    dbContext.Entry<E>(entity).State = EntityState.Modified;
    dbContext.SaveChanges();
}

I think that's happening because EF doesn't track the items, but I don't know what can I do. What's the best approach to initialize the dummy data and get tracked by EF? Thanks.

Upvotes: 0

Views: 1118

Answers (2)

CodeCaster
CodeCaster

Reputation: 151594

I think that's happening because EF doesn't track the items

This is happening because EF is tracking entities. Using the same DbContext instance you're trying to read and update an entity through different instances of that entity. Entities are tracked by reference.

This means that the entity you pass to this Update() method is different from the one you read from the database in the first place.

If you don't do that explicitly in code, it may be that the code goes through a mapping phase where the entity-to-update is new'ed (either explicitly or through reflection) and populated from another entity, for example a viewmodel that is mapped through AutoMapper.

Another scenario is where the context has a too long lifetime, for example because it's used from a static variable and/or singleton or a incorrectly configured dependency injection container.

So either:

  • Fix the lifetime of the DbContext so the lookup and the update happen on different instances of the context.
  • Make sure you update the entity returned from the first lookup, if the lookup has to happen on the same DbContext instance as the update.
  • Use AsNoTracking() at the lookup. Querying the same entity multiple times may make sense, in which case using non-tracked entities in certain locations may be the solution.

Whichever approach you choose, you need to deal with concurrency, which you don't want to handle yourself. Optimistic concurrency support is present by default in Entity Framework.

Upvotes: 1

federico scamuzzi
federico scamuzzi

Reputation: 3778

I suggest you to do a select (with EF) before send the entity to your Update method then also try to attach the entity to the context after set it as modified (it depend if you use changetraking options or not)

 dbContext.Set<TEntity>().Attach(entity);

for example this is my Repository Update method (just for an example):

public void Update(TEntity updated, Tkey key)
        {
            updated.ObjectState = ObjectState.Modified;

            var existing = _dbSet.Find(key);
            if (existing == null) return;

            existing.ObjectState = ObjectState.Modified;
            dbContext.Entry(existing).CurrentValues.SetValues(updated);
            dbContext.Set<TEntity>().Attach(existing);



        }

Upvotes: 0

Related Questions