2012-04-03 316 views
0

我正在開發一個ASP.Net MVC 3 Web應用程序使用實體框架4.1,但是,我有更新實體的導航實體的問題。實體框架更新導航實體

我有一個實體,稱爲移

public partial class Shift 
{ 
    public Shift() 
    { 
     this.Locations = new HashSet<ShiftLocation>(); 
    } 

    public int shiftID { get; set; } 
    public string shiftTitle { get; set; } 

    public virtual ICollection<ShiftLocation> Locations { get; set; } 

} 

在我的換檔控制器,我有兩個HttpPost方法來創建和編輯的轉變。

[HttpPost] 
    public ActionResult CreateShift(ViewModelShift model) 
    { 

      if (ModelState.IsValid) 
      { 
       Shift shift = new Shift(); 
       shift = Mapper.Map<ViewModelShift, Shift>(model); 

       //Create new shift location and add it to the newly created Shift 
       ShiftLocation location = new ShiftLocation(); 
       location.locationID = model.locationID; 
       shift.Locations.Add(location); 

       _shiftService.AddShift(shift); 

       return RedirectToAction("Index"); 
      } 
} 

[HttpPost] 
    public ActionResult EditShift(ViewModelShift model) 
    { 

      if (ModelState.IsValid) 
      { 
       Shift shift = new Shift(); 
       shift = Mapper.Map<ViewModelShift, Shift>(model); 


       ShiftLocation location = new ShiftLocation(); 
       //location = Mapper.Map<ViewModelShift, ShiftLocation>(model); 
       location.shiftID = model.shiftID; 
       location.locationID = model.locationID; 
       shift.Locations.Add(location); 

       _shiftService.UpdateShift(shift); 

       return RedirectToAction("Index"); 
      } 
     } 

的CreateShift法正常工作,即插入一條記錄到數據庫中的一個新的轉變,也爲另一個表內的移位位置上的新紀錄。但是,EditShift方法僅更新Shift記錄,但它不會更新Shift位置記錄,即使我已分配了shiftID和新的locationID。

關於如何解決這個問題的任何信息將不勝感激。

謝謝。

編輯

我ShiftLocation類是如下

public partial class ShiftLocation 
{ 
    public int shiftLocationID { get; set; } 
    public int shiftID { get; set; } 
    public int locationID { get; set; } 

    public virtual Shift Shift { get; set; } 
} 

的ShiftService類中的UpdateShift方法如下

public void UpdateShift(Shift item) 
    { 
     _UoW.Shifts.Update(item); 
     _UoW.Commit(); 
    } 

這然後調用Update方法我通用庫

public void Update(TEntity entityToUpdate) 
    { 
     dbSet.Attach(entityToUpdate); 
     context.Entry(entityToUpdate).State = EntityState.Modified; 
    } 

第二編輯

從Slauma的回答繼我編輯EditShift POST操作以下

if (ModelState.IsValid) 
      { 
       //Shift shift = new Shift(); 
       Shift shift = _shiftService.GetShiftByID(model.shiftID); 
       ShiftLocation shiftLocation = shift.Locations.Where(s => s.shiftID == model.shiftID).Single(); 

       shift = Mapper.Map<ViewModelShift, Shift>(model); 

       shiftLocation.locationID = model.locationID; 
       shift.Locations.Add(shiftLocation); 

       _shiftService.UpdateShift(shift); 

       return RedirectToAction("Index"); 
      } 

我現在的問題是,當我在通用存儲庫更新方法被調用,以下發生錯誤

An object with the same key already exists in the ObjectStateManager. 
The ObjectStateManager cannot track multiple objects with the same key 

現在,我現在爲什麼會發生這種情況,因爲在我的EditPost操作中,我檢索Shift的一個實例,然後在通用庫中的更新方法中,我試圖附加相同的Shift。

我現在只是很困惑。 My Generic Repositro看起來不錯,因爲我從本文頂部提到的Microsoft MVC教程中複製了它。

再次有關如何獲得這個簡單的更新工作的任何幫助將不勝感激。

+0

您可以顯示'ShiftLocation'類和'_shiftService.UpdateShift(移)'裏面的代碼? – Slauma 2012-04-03 18:11:46

+0

@Slauma - 請參閱我的編輯。謝謝。 – tgriffiths 2012-04-03 19:56:36

+0

你想要達到什麼目的?想象一下,「shift」存儲在數據庫中,並且在DB中已經有5個「ShiftLocations」分配給它。現在你到達'EditShift'後期操作。在你的代碼中,你創建一個'ShiftLocation'對象並將它添加到'shift.Locations'集合中。您現在需要:1)將這個新的位置添加到已有的位置,或者2)您想刪除現有的5個位置並將其替換爲新的位置,或者3)您是否希望找到位置爲其中一個(通過Id?),然後用新值更新它? – Slauma 2012-04-03 20:49:24

回答

1

關於你的評論...

當我到了「EditShift」後的行動,我想更新了「移」 與從通過檢索在 「ViewModelShift新的細節模型'。這個ViewModel也將包含 'locationID'只有'ShiftLocation',但是,這個ShiftLocation 已經存在於數據庫中,我只是想用新的 locationID更新它。

...我想試試這個(忽略你的資料庫結構):

[HttpPost] 
public ActionResult EditShift(ViewModelShift model) 
{ 
    if (ModelState.IsValid) 
    { 
     Shift shift = context.Shifts 
      .Include(s => s.Locations) 
      .Single(s => s.shiftID == model.shiftID); 

     context.Entry(shift).CurrentValues.SetValues(model); 

     ShiftLocation location = shift.Locations.Single(); 
     // because you expect exactly one location 

     location.locationID = model.locationID; 

     context.SaveChanges(); 

     return RedirectToAction("Index"); 
    } 

    //... 
} 

設置shiftModified的狀態不工作,因爲它不影響任何相關的實體。該位置仍然處於Unchanged狀態。您可能也將位置的狀態設置爲Modified,但它看起來像您的ViewModel中沒有主鍵屬性shiftLocationID。所以,找到正確位置更新的唯一方法是通過shift的導航屬性(在上例中= Include)加載它。

編輯

在你的第二編輯代碼不起作用,因爲AutoMapper創建的shift一個新的實例,以後你重視這種轉變的背景下。但是由於您已經從數據庫中加載了shift,因此該shift也會附加到上下文中。您無法將兩個不同的實例附加到上下文中。這導致異常。

我上面的代碼應該可以工作,我嘗試使用你的服務和通用存儲庫來重寫它。兩者都必須擴展,因爲服務和存儲庫在執行任務的功能上似乎不夠豐富。尤其是通用的Update太弱,無法更新對象圖(根對象+一個或多個導航屬性)。

[HttpPost] 
public ActionResult EditShift(ViewModelShift model) 
{ 
    if (ModelState.IsValid) 
    { 
     Shift shift = Mapper.Map<ViewModelShift, Shift>(model); 
     _shiftService.UpdateShiftAndLocation(shift, model.LocationID); 

     return RedirectToAction("Index"); 
    } 

    //... 
} 

創建新的服務方法:

public IQueryable<T> Find(Expression<Func<T, bool>> predicate, 
    params Expression<Func<T, object>>[] includes) 
{ 
    return dbSet.IncludeMultiple(includes) 
     .Where(predicate); 
} 

(從這裏的IQueryabe<T>使用拉吉斯拉夫的IncludeMultiple擴展方法:https://stackoverflow.com/a/5376637/270591

在通用倉庫

public void UpdateShiftAndLocation(Shift detachedShift, int locationID) 
{ 
    Shift attachedShift = _UoW.Shifts.Find(
     s => s.ShiftID == detachedShift.ShiftID, s => s.Locations); 

    _UoW.Shifts.UpdateFlatProperties(attachedShift, detachedShift); 

    ShiftLocation location = attachedShift.Locations.Single(); 
    // MUST be exactly one location for the Shift in the DB 
    // ToDo: Catch the case somehow, if there is no or more than one location 
    location.LocationID = locationID; 

    _UoW.Commit(); 
} 

Find方法

UpdateFlatProperties方法在通用存儲庫:

// "Flat" means: Scalar and Complex properties, but not Navigation properties 
public void UpdateFlatProperties(T attachedEntity, T detachedEntity) 
{ 
    context.Entry(attachedEntity).CurrentValues.SetValues(detachedEntity); 
} 
+0

請參閱我上面的第二編輯。還有什麼想法?謝謝。 – tgriffiths 2012-04-05 11:50:28

+0

@tgriffiths:你可以仔細檢查第二次編輯中的代碼嗎?不知何故,它沒有任何意義:您將'shiftLocation'添加到'shift.Locations'集合中,但之前從完全相同的集合中檢索到'shiftLocation'。另外,你實例化一個新的'location',但是對這個實例不做任何事情。 – Slauma 2012-04-05 12:01:15

+0

我刪除了實例的位置,因爲它沒有做任何事情。我將shiftLocation添加到shift位置,因爲我的AutoMapper不會爲我執行此操作。一旦Mapper.Map代碼運行,shift.Locations = 0,因此我將shiftLocation添加到新更新的'locationID'中以便shift.Locations。但問題仍然存在,即在進行更新時,ObjectStateManager中已存在密鑰。 – tgriffiths 2012-04-05 13:14:13