簡體   English   中英

實體框架更新多對多關系的中間表

[英]Entity Framework update the intermediate table of a many-to-many relation

我在userproject之間建立了多對多關系。 喜歡:

class User 
{
    public ICollection<Project> Projects { get; set; }
}

class Project 
{
    public ICollection<User> Users { get; set; }
}

實體框架自動生成中間表。

問題是我想更新用戶以及整個項目列表。 可以以任何方式修改此列表,可以添加和刪除項目。 同時在用戶 object 更新之前。

我總是遇到同樣的錯誤,即 Entity Framework 試圖在中間表中添加重復的條目。

我嘗試了很多事情都沒有成功(下面列出了一些)。

var tmp = Context.Entry(user); // user being the updated object.
tmp.State = EntityState.Modified;
tmp.Collection(e => e.Projects).IsModified = true;
        
Context.Users.Update(user);
Context.SaveChanges();

或者

var tmp = Context.Users.SingleOrDefault(u => u.Id == user.Id);

if (tmp == null) 
     return null;

Context.Entry(tmp).CurrentValues.SetValues(user);
Context.SaveChanges();

return user;

或者只是簡單的舊更新:

Context.Users.Update(user);
Context.SaveChanges();

但這些都不起作用。

這個問題聽起來像是您有一個帶有一組項目的分離的用戶實體,並且您想將其傳遞到一個方法中,與 DbContext 關聯以保留更改。

您遇到加倍記錄的問題,因為當您將用戶附加到 DbContext 時,它會將與用戶關聯的每個項目實體視為新實例,因為它們本身不引用跟蹤實例。

使用關聯更新分離的實體是相當復雜的,尤其是在您希望可能在操作中添加或刪除關聯的情況下。

推薦的方法是從數據庫加載當前用戶項目,然后利用 Automapper 來保護您可以從分離的實體復制哪些值,然后 go 通過關聯添加/刪除任何已更改的項目引用。 如果可以創建一個全新的項目作為此操作的一部分與用戶關聯,那么您也需要處理它。

var existingUser = Context.Users.Include(x => x.Projects).Single(x => x.UserId == user.UserId);
Mapper.Map(user, existingUser); 
// Where Automapper is configured with a User to User mapping with allowed 
// values to copy over, ignoring anything that cannot legally be changed.

var newProjectIds = user.Projects.Select(x => x.ProjectId).ToList();
var existingProjectIds = existingUser.Projects.Select(x => x.ProjectId).ToList();
var projectIdsToAdd = newProjectIds.Except(existingProjectIds).ToList();
var projectIdsToRemove = existingProjectIds.Except(newProjectIds).ToList();

var projectsToAdd = Context.Projects.Where(x => projectIdsToAdd.Contains(x.ProjectId)).ToList();
var projectsToRemove = existingUser.Projects.Where(x => projectIdsToRemove.Contains(x.ProjectId)).ToList();

foreach(var project in projectsToRemove)
    existigUser.Projects.Remove(project);
foreach(var project in projectsToAdd)
    existingUser.Projects.Add(project);

Context.SaveChanges();

... 這個例子不包括全新項目的可能性。 如果更新后的用戶可以包含一個全新的項目,那么您需要在查找 projectsToAdd 時檢測這些項目,以從傳入的項目列表中添加任何項目,其中 ID 在新項目 ID 中但未在數據庫中找到。 這些分離的引用可以添加到從 DbContext 加載的用戶,但是您確實需要處理每個項目可能必須避免重復的任何導航屬性,將每個屬性替換為對跟蹤實體的引用,包括返回到的任何雙向引用用戶(如果存在)。

通常,處理分離的實體有多種注意事項,您需要牢記並慎重處理。 通常最好避免傳遞分離的實體,而是旨在傳遞您想要關聯的數據的最小表示,然后加載和調整該服務器端。 通常使用分離實體的論據是避免必須再次加載數據,但是這會導致在嘗試同步這些分離實例時需要更多代碼,並且忽略了數據 state 自分離實例被采用后可能已經更改的事實。 例如,上面的代碼還應該查看分離的實體和加載的 state 之間的實體版本控制,以檢測是否有其他人可能進行了更改,因為分離的副本在用戶進程開始時被讀取以進行更改。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM