I'm using Entity Framework Core together with the repository pattern. To help me out, I coded one base repository with the basic CRUD methods. The update method is as follows:
public void Update(TEntity entity)
{
var contextEntry = _context.Entry<TEntity>(entity);
if (contextEntry.State == EntityState.Dettached)
{
_context.Attach(entity);
}
contextEntry.State = EntityState.Modified;
_context.SaveChanges();
}
Given the BaseRepository
class containing this method, I created one User
repository inheriting from this
public class UserRepository : BaseRepository<User>, IUserRepository
{
}
And I've used this in the PUT method of one Web API coded with ASP.NET Core
[HttpPut("~/api/users/{id}")]
public IActionResult Put(int id, [FromBody] User user)
{
if (user == null || user.UserId != id)
{
return BadRequest();
}
userRepository.Update(user);
return new NoContentResult();
}
Now, when issuing a request, I get one error in the _context.Attach(entity)
line. The exception says that it can't add the entity for tracking because there is already another entity with the same key being tracked.
When debugging I saw that contextEntry.State
was set to Unchanged
. Hence, it is obviously not equal to EntityState.Dettached
. Still, the execution got inside the if
statement and tried to attach the entity.
Something is quite wrong here. Is this a bug? Or am I doing something very wrong? I believe that I'm the one doing something very wrong with this update strategy, but I'm unsure about it. In that case, what is wrong with my approach?
EDIT: I updated the Update
method to use just _context.Update(entity)
and after _context.SaveChanges()
. Still, the _context.Update(entity)
throws one InvalidOperationException
with this message:
Additional information: The instance of entity type 'User' cannot be tracked because another instance of this type with the same key is already being tracked. When adding new entities, for most key types a unique temporary key value will be created if no key is set (i.e. if the key property is assigned the default value for its type). If you are explicitly setting key values for new entities, ensure they do not collide with existing entities or temporary values generated for other new entities. When attaching existing entities, ensure that only one entity instance with a given key value is attached to the context.
All the information is in the exception message... you already have another copy of the entity with that primary key attached.
I would recommend one of the following (preferred first):
.Set<TEntity>.Find(object[] key)
on your context using the Primary Key, in order to retrieve any entity you already have.Set<TEntity>.Local.Find(..)
to check if it already exists