1

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, Func2 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?

Sam
  • 4,766
  • 11
  • 50
  • 76
  • See http://stackoverflow.com/questions/36369233/entity-framework-core-1-0-currentvalues-setvalues-does-not-exist – Krazibit312 Feb 08 '17 at 21:02

2 Answers2

0
public async Task<Employee> Update(Employee emp)
{
    _context.Entry(emp).State = EntityState.Modified;
    await _context.SaveChangesAsync();
    return employee;
}
Dmitry
  • 16,110
  • 4
  • 61
  • 73
  • Welcome to stack overflow :-) Please look at [answer]. You should provide some information why your code solves the problem. Code-only answers aren't useful for the community. – JimHawkins Feb 09 '17 at 09:24
  • "Any answer that gets the asker going in the right direction is helpful" – Dmitry Feb 09 '17 at 09:36
  • 1
    with respect to your reputation, I think you know what I mean. Stack overflow isn't a simple "Q & A" forum, but meant as a knowledge base for other people than the one who asked the question. **Excerpt from [answer]:** _"Brevity is acceptable, but fuller explanations are better."_ . I didn't downvote your answer, and I didn't want to offend you. – JimHawkins Feb 09 '17 at 09:43
0

I had to change my Repo to be Scoped rather than singleton:

From

services.AddSingleton<IEmployeeRepository, EmployeeRepository>();

to:

services.AddScoped<IEmployeeRepository, EmployeeRepository>();
Sam
  • 4,766
  • 11
  • 50
  • 76
  • Did this solution work? I cannot see repository in any of the code you posted above. – Smit Feb 10 '17 at 01:59