2012-03-07 86 views
5

我目前使用EF4.3和Code First。我的對象創建工作(通過我的視圖 - 只使用自動生成的創建),但是當我試圖編輯一個對象時,它不會保存任何更改,這些更改很快會與我的導航屬性相關聯。我一直在reading on relationships,但我不明白如何告訴我的背景,這種關係已經改變。無法獲取關係更新實體框架中的導航屬性

這裏是我的實現的一些示例代碼。

@* Snippet from my view where I link into my ViewModel. *@ 
<div class="row"> 
    <div class="editor-label"> 
     @Html.LabelFor(model => model.ManagerID) 
    </div> 
    <div class="editor-field"> 
     @Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty) 
     @Html.ValidationMessageFor(model => model.ManagerID) 
    </div> 
</div> 

這是我的控制器實現(我的編輯的POST):

[HttpPost] 
    public ActionResult Edit(ProjectViewModel projectViewModel) 
    { 
     if (ModelState.IsValid) 
     { 
      Project project = new Project(); 
      project.ProjectID = projectViewModel.ProjectID; 
      project.Name = projectViewModel.Name; 
      project.ProjectManager = repository.GetUser(projectViewModel.ManagerID); 
      repository.InsertOrUpdateProject(project); 
      repository.Save(); 
      return RedirectToAction("Index"); 
     } 
     ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID); 
     return View(projectViewModel); 
    } 

在我的項目對象:

public class Project 
{ 
    public int ProjectID { get; set; } 
    [Required] 
    public string Name { get; set; } 

    // Navigation Properties 
    public virtual User Manager { get; set; } 
} 

下面是從儲存庫中的相應的方法(其中,我的上下文):

public void InsertOrUpdateProject(Project project) 
    { 
     if (program.ProjectID == default(int)) 
     { 
      context.Projects.Add(project); 
     } 
     else 
     { 
      context.Entry(project).State = EntityState.Modified; 
     } 
    } 

爲了清楚起見,這可以更新我的屬性,但它不會更新我的導航屬性(在本例中爲管理器)。感謝任何幫助。

回答

11

將狀態設置爲Modified只會將標量屬性標記爲已修改標記屬性,而不是導航屬性。您有幾種選擇:

  • 一個黑客(你不會喜歡它)

    //... 
    else 
    { 
        var manager = project.Manager; 
        project.Manager = null; 
        context.Entry(project).State = EntityState.Modified; 
        // the line before did attach the object to the context 
        // with project.Manager == null 
        project.Manager = manager; 
        // this "fakes" a change of the relationship, EF will detect this 
        // and update the relatonship 
    } 
    
  • 刷新從數據庫項目包括(立即加載)的現任經理。然後設置屬性。更改跟蹤將再次檢測到管理器的更改並寫入UPDATE。

  • 揭露了Manager導航屬性的外鍵屬性在你的模型:

    ​​

    現在ManagerID是標量屬性和狀態設置爲Modified將這一屬性。而且你不需要從數據庫加載Manager用戶,你可以指定你從你的觀點得到ID:

    Project project = new Project(); 
    project.ProjectID = projectViewModel.ProjectID; 
    project.Name = projectViewModel.Name; 
    project.ManagerID = projectViewModel.ManagerID; 
    repository.InsertOrUpdateProject(project); 
    repository.Save(); 
    
+0

非常好。謝謝。我原本完成了外鍵路線,但遇到了一些問題。事實證明,它的工作原理,但我必須定義導出屬性的[ForeignKey(「Name」)]屬性,以正確映射一切。接受你的答案。謝謝! – glockman 2012-03-07 20:29:42

+0

@ user1240560:在我的答案中的名稱實際上應該沒有ForeignKey屬性(通過名稱約定檢測FK)。但是,如果你的名字有些不同,並且不遵循約定規則,那麼是的,你需要註釋。 – Slauma 2012-03-07 21:18:58

+0

@Slauma我愛你,其他方法應該與Code First Model一起工作,但是使用DataBase首先,唯一對我有用的是你的黑客,我已經搞了好幾天,現在我不得不說我愛你這麼多 – 2014-05-19 01:21:07

-2

我不確定導航屬性究竟是什麼意思?你的意思是像一個外鍵關係?如果是這樣,請嘗試以下數據註釋:

public class Project 
{ 
    public int ProjectID { get; set; } 

    [Required] 
    public string Name { get; set; } 

    [ForeignKey("YourNavigationProperty")] 
    public virtual UserManager { get; set; } 
} 

更新您的EF上下文,看看會發生什麼?

UPDATE

public class Project 
{ 
    public int ProjectID { get; set; } 

    [Required] 
    public string Name { get; set; } 

    [ForeignKey("ManagerId")] 
    public ManagerModel UserManager { get; set; } 
} 

public class ManagerModel 
{ 
    [Key] 
    public int ManagerId { get; set; } 

    public String ManagerName { get; set; } 
} 

看看是否能工作?

+0

所以我沒有明確定義我的FK名稱。我希望能夠將我的管理器值設置爲一個新的管理器(似乎可以正常工作),但是當我調用SaveChanges()時,這些更改實際上不會保留到數據庫中。我不知道如何做到這一點。 – glockman 2012-03-07 18:35:05

+0

查看我的回答更新 – jacqijvv 2012-03-07 19:11:06

2

有幾個選項在這裏,我將列出其中的三個:

選項1:使用GraphDiff

*這需要你的背景設置爲true的Configuration.AutoDetectChangesEnabled

只需用的NuGet

Install-Package RefactorThis.GraphDiff 

然後

using (var context = new Context()) 
{ 
    var customer = new Customer() 
    { 
     Id = 12503, 
     Name = "Jhon Doe", 
     City = new City() { Id = 8, Name = "abc" } 
    }; 

    context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City)); 
    context.Configuration.AutoDetectChangesEnabled = true; 

    context.SaveChanges(); 
} 

安裝GraphDiff有關GraphDiff更詳細看here

選項2:查找和編輯

用EF搜索實體以將其跟蹤到上下文。然後編輯屬性。

*這需要您的上下文的Configuration.AutoDetectChangesEnabled設置爲true。

var customer = new Customer() 
{ 
    Id = 12503, 
    Name = "Jhon Doe", 
    City = new City() { Id = 8, Name = "abc" } 
}; 

using (var context = new Contexto()) 
{ 
    var customerFromDatabase = context.Customers 
             .Include(x => x.City) 
             .FirstOrDefault(x => x.Id == customer.Id); 

    var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id); 

    customerFromDatabase.Name = customer.Name; 
    customerFromDatabase.City = cityFromDataBase;     

    context.Configuration.AutoDetectChangesEnabled = true; 
    context.SaveChanges(); 
} 

選項3:使用一個標量屬性

在性能這個問題是最好的方式,但它惹你與數據庫的關注類。因爲您需要創建一個標量(原語類型)屬性來映射Id。

*這樣就不需要將Configuration.AutoDetectChangesEnabled設置爲true。此外,您不需要對數據庫執行查詢以檢索實體(因爲前兩種選擇會 - GraphDiff在後臺執行!)。

var customer = new Customer() 
{ 
    Id = 12503, 
    Name = "Jhon Doe", 
    City_Id = 8, 
    City = null 
}; 

using (var contexto = new Contexto()) 
{ 
    contexto.Entry(customer).State = EntityState.Modified; 
    contexto.SaveChanges(); 
}