I have an update method in my Web API repository that looks like this (in EF Core):
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
employee.FirstName = emp.FirstName;
employee.LastName = emp.LastName;
employee.Supervisor = emp.Supervisor;
employee.OfficeBureau = emp.OfficeBureau;
employee.Notes = emp.Notes;
await _context.SaveChangesAsync();
return employee;
}
It works well enough. But do we really have to do this? I want to do something more like this and update all entity properties in one shot:
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
employee = emp;
_context.Update(employee);
await _context.SaveChangesAsync();
return employee;
}
I shouldn't even need this: employee = emp;
All I should need is this: _context.Update(emp);
So EF should say, hey, I need to update an Employee object and I know which one it is by the ID of emp you passed me on update. But I just can't get it to work.
Does anyone know how to do this or am I really supposed to do it like in the first option?
The answer below from Dmitry is not working.
If I put a break point here: Employee employee = await _context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
and then try and step though, execution seems to get swallowed up on this line:
_context.Entry(emp).State = EntityState.Modified;
and then this is the response returned: {}
and the employee in the Database is unchanged.
Also tried this:
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
EntityEntry<Employee> entity = _context.Employees.Attach(emp);
entity.State = EntityState.Modified;
_context.Employees.Update(emp);
await _context.SaveChangesAsync();
return employee;
}
But same thing. Execution gets swallowed up here:
EntityEntry<Employee> entity = _context.Employees.Attach(emp);
Where is the execution going? Hard to tell with async sometimes.
I got it to work once like this. Funny, I got to work right off the bat when I put it in a try/catch.
public async Task<Employee> Update(Employee emp)
{
Employee employee = await
_context.Employees.SingleOrDefaultAsync(e => e.ID == emp.ID);
if (employee == null)
{
return null;
}
try
{
_context.Employees.Update(emp);
}
catch(Exception ex)
{
throw ex;
}
await _context.SaveChangesAsync();
return emp;
}
But it only worked once. Now it keeps throwing this exception.
{System.InvalidOperationException: The instance of entity type 'Employee' 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.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph(EntityEntryGraphNode node, Func
2 handleNode)
at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) at Lerd.Models.Concrete.EmployeeRepository.d__4.MoveNext()}"
How do I get rid of this so dbContext.Update works everytime?