实体框架更新导航实体

问题描述:

我正在开发一个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

关于你的评论...

当我到了“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://*.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