Kuba Kuba - 2 months ago 58
C# Question

Entity Framework Core - update related collection

I'm trying to update collection of ProjectEmployees inside ProjectModel.
I want to remove all old values and set new.

My models:

public class Project
{
...
public ICollection<ProjectEmployee> ProjectEmployees { get; set; }
}

public class ProjectEmployee
{
public int ProjectId { get; set; }
public virtual Project Project { get; set; }
public int UserId { get; set; }
public virtual Employee Employee { get; set; }
}

public class Employee
{
public int UserId { get; set; }
public User User { get; set; }
...
}
public class ProjectGroupModel //ViewModel
{
public int ProjectId { get; set; }
public ICollection<Employee> ProjectEmployees { get; set; }
}


It's typical many-to-many relationship.

My controller action:

[HttpPost("group")]
public async Task<IActionResult> CreateGroup([FromBody] ProjectGroupModel pro)
{
var dbProject = await _context.Project
.Include(p=>p.ProjectEmployees)
.FirstAsync(p => p.ProjectId == pro.ProjectId);
dbProject.ProjectEmployees.Clear();

foreach (var emp in pro.ProjectEmployees)
{
dbProject.ProjectEmployees.Add(new ProjectEmployee()
{
UserId = emp.UserId
});
}

await _context.SaveChangesAsync();

return Ok();
}


When pro.ProjectEmployees is empty all records from dbProject.ProjectEmployees were removed correctly, also if dbProject.ProjectEmployees is empty new records from model were added, but when dbProject.ProjectEmployees is not empty I can't set new records:

ERROR:


"The instance of entity type 'ProjectEmployee' 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."


I tried to repair this action in a hundreds way but always sth is wrong.

Answer

It's far from perfect, but the only way I was able to make it work is to remove the items from the corresponding DbSet and call SaveChanges before adding the new ones:

var dbProject = await _context.Project
    .Include(p=>p.ProjectEmployees)
    .FirstAsync(p => p.ProjectId == pro.ProjectId);

if (dbProject.ProjectEmployees.Any())
{
    _context.ProjectEmployee.RemoveRange(dbProject.ProjectEmployees);
    await _context.SaveChangesAsync();
}

foreach (var emp in pro.ProjectEmployees)
{
    dbProject.ProjectEmployees.Add(new ProjectEmployee()
    {
        UserId = emp.UserId
    });
}

await _context.SaveChangesAsync();