回答

1

使用外鍵更新實體優於導航屬性,因爲您不必處理實體的狀態。使用導航屬性更新實體時最常見的問題是在您可能預期相反的情況下獲取重複記錄。假設你有這個簡單的模型:

public class Post 
{ 
    public int Id {get; set;} 
    public string Title {get; set;} 

    [ForeignKey("CategoryId")] 
    public Category Category {get; set;} 

    public int CategoryId {get; set;} 
} 


public class Category 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 
} 

現在讓我們假設你有一個更新Post的控制器動作。認爲視圖有列出所有類別的列表框,用戶可以更改它。

PostController中立即更新

[HttpPost] 
public ActionResult UpdatePost(PostDTO, post) 
{ 
    //Retrived the chosen category using its own "repository" class 
    Category newCategory = categoryRepository.Get(post.CategoryId); 

    //other validations here.. 

    //Call method to update passing postDTO and the existing category 
    postRepository.Update(post, newCategory) 
} 

,在PostRepository,你做這樣的事情:

public void Update(PostDTO postDTO, Category category) 
{ 
    using (var context = new ScreencastContext()) 
    { 
    //Get the post in this context 
     var post = context.Post.SingleOrDefault(x => x.Id == postDTO.Id); 

     post.Name = postDTO.Name; 
     //set other Post fields... 

     //Set the new category to the post 
     //this category already exists in the database and was retrieved by another "context" 
     post.Category = category; 

     context.Post.Attach(post); 
     context.Entry(post).State - EntityState.Modified; 

     context.SaveChanges(); 
    } 
} 

也許你會認爲實體框架將 「趕上」 你的等級對象已經存在於數據庫中,並且只會更新Post表中的外鍵標識。 錯誤。 它實際上會創建一個新的類別,這是因爲當您從另一個上下文中檢索該類別時,此上下文不會將其識別爲圖形的一部分,而無法識別的實體具有默認狀態「已添加」。所以當你調用SaveChanges時,它會創建一個新的類別。

你可以自己管理實體的狀態來解決這個問題,但是這會變得非常複雜,並且很容易被許多怪異的代碼弄得無法理解。這就是外鍵派上用場的時候。

以上更新方法可以rewriten到這樣的事情:

public void Update(PostDTO postDTO, Category category) 
{ 
    using (var context = new ScreencastContext()) 
    { 
     //Get the post in this context 
     var post = context.Post.SingleOrDefault(x => x.Id == postDTO.Id); 

     post.Name = postDTO.Name; 
     //set other Post fields... 

     //Set the new category to a post 
     //this category already exists in the database and was retrived by another "context" 
     post.CategoryId = category.Id; 

     //This just prevent any accidental navigation property being set. 
     post.Category = null; 

     context.Post.Attach(post); 
     context.Entry(post).State = EntityState.Modified; 

     context.SaveChanges(); 
    } 
} 

這樣,你只更新後的表。一個新的類別將不會創建,並且一切都將按預期工作。

這件事,我發現在我自己的工作場所發生,很多時候,我總是指人這個驚人文章由朱莉·勒曼寫:

Why Does Entity Framework Reinsert Existing Objects into My Database?

+0

這太神奇了,謝謝。 如果您僅使用類別ID,爲什麼要進行此調用: 類別newCategory = categoryRepository.Get(post.CategoryId); 僅通過Id並避免此調用並不是更高效? –

+0

是的,如果你可以保證傳遞給控制器​​的Id是一個實際存在的Id,你可以跳過這個呼叫。我只是這樣做的,所以你可以理解查詢另一個類的數據並在其他類中使用它的「上下文」問題。還有一個併發問題:如果有人在更新Post時刪除了該類別,該怎麼辦?這就是爲什麼你再次檢查。 – jpgrassi

+0

哇,這太好了,我有一個可以肯定會隨時刪除的物品。所以,貨幣可能是一個嚴重的問題。 非常感謝,我確認,你給的文章寫得非常好。 –

相關問題