Sto cercando di aggiornare la raccolta di ProjectEmployees all'interno di ProjectModel. Voglio rimuovere tutti i vecchi valori e impostare nuovi.
I miei modelli:
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; }
}
È la tipica relazione molti-a-molti.
Azione del mio controller:
[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();
}
Quando pro.ProjectEmployees è vuoto, tutti i record da dbProject.ProjectEmployees sono stati rimossi correttamente, anche se dbProject.ProjectEmployees è vuoto sono stati aggiunti nuovi record dal modello, ma quando dbProject.ProjectEmployees non è vuoto Non posso impostare nuovi record:
ERRORE:
"L'istanza del tipo di entità 'ProjectEmployee' non può essere rintracciata perché viene già tracciata un'altra istanza di questo tipo con la stessa chiave. Quando si aggiungono nuove entità, per la maggior parte dei tipi di chiave verrà creato un valore chiave temporaneo univoco se non viene impostata alcuna chiave ( vale a dire se alla proprietà della chiave viene assegnato il valore predefinito per il suo tipo) Se si impostano esplicitamente valori chiave per le nuove entità, assicurarsi che non entrino in collisione con entità esistenti o valori temporanei generati per altre nuove entità. solo un'istanza dell'entità con un dato valore chiave è allegata al contesto. "
Ho provato a riparare questa azione in un modo diverso, ma sempre sth è sbagliato.
È ben lungi dall'essere perfetto, ma l'unico modo in cui sono riuscito a farlo funzionare è rimuovere gli elementi dal DbSet
corrispondente e chiamare SaveChanges
prima di aggiungerne di nuovi:
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();
collegato a un'altra domanda SO
Posso rispondere qui con le tue lezioni.
Quindi utilizzare questa estensione ho fatto per rimuovere la deselezionata e aggiungere il nuovo selezionato alla lista
public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
}
public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
return items
.GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
.SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
.Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
.Select(t => t.t.item);
}
usarlo sembra così
var model = db.Employees
.Include(x => x.ProjectEmployees)
.FirstOrDefault(x => x.EmployeeId == employee.EmployeeId);
db.TryUpdateManyToMany(model.ProjectEmployees, listOfNewProjectIds
.Select(x => new ProjectEmployee
{
ProjectId = x,
EmployeeId = employee.EmployeeId
}), x => x.ProjectId );