[英]Entity Framework Core - update related collection
我正在嘗試更新ProjectModel中ProjectEmployees的集合。 我想刪除所有舊值並設置新值。
我的模特:
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; }
}
這是典型的多對多關系。
我的控制器動作:
[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();
}
當pro.ProjectEmployees為空時,dbProject.ProjectEmployees中的所有記錄都被正確刪除,如果dbProject.ProjectEmployees為空,則添加了來自model的新記錄,但是當dbProject.ProjectEmployees不為空時,我無法設置新記錄:
錯誤:
“無法跟蹤實體類型'ProjectEmployee'的實例,因為已經跟蹤了具有相同密鑰的此類型的另一個實例。添加新實體時,對於大多數密鑰類型,如果未設置密鑰,則將創建唯一的臨時密鑰值(即,如果為鍵屬性指定了其類型的默認值,則。如果要為新實體顯式設置鍵值,請確保它們不會與現有實體或為其他新實體生成的臨時值發生沖突。附加現有實體時,請確保只有一個具有給定鍵值的實體實例附加到上下文。“
我試圖以數百種方式修復這個動作,但總是錯誤的。
與另一個SO問題相關聯
我可以在這里和你的班級一起回答。
然后使用此擴展我刪除未選中並將新選擇添加到列表中
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);
}
使用它看起來像這樣
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 );
它遠非完美,但我能夠使其工作的唯一方法是從相應的DbSet
刪除項目並在添加新的之前調用SaveChanges
:
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();
我認為舊的對象並沒有真正從數據庫中刪除。 你只是調用Clear()這還不夠。 試着這樣做:
[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);
foreach (var old in dbProject.ProjectEmployees)
{
_context.ProjectEmployee.Remove(old);
}
dbProject.ProjectEmployees.Clear();
foreach (var emp in pro.ProjectEmployees)
{
dbProject.ProjectEmployees.Add(new ProjectEmployee()
{
UserId = emp.UserId
});
}
await _context.SaveChangesAsync();
return Ok();
}
在這里,我假設您在上下文中有適當的設置(_context.ProjectEmployee)。 如果您在上下文中沒有明確地使用此Set,則只需添加它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.