【问题标题】:EF4 Context.ApplyCurrentValues does not update current valuesEF4 Context.ApplyCurrentValues 不更新当前值
【发布时间】:2010-10-28 00:05:41
【问题描述】:

我有一个实体,我检索如下并与上下文分离:

ctx.Reviews.MergeOption = MergeOption.NoTracking;

Review review = (from r in ctx.Reviews.Include("ReviewNotes")
                 where r.ReviewID == reviewID
                 select r).First();

然后我对关系中的一个对象进行更改:

if (review.ReviewNotes.Count > 0)
{
  ReviewNote r = review.ReviewNotes.ElementAt(0);
  r.Note = "Ugg " + DateTimeOffset.Now.ToString();
  r.CreatedDate = DateTimeOffset.Now;
}

然后我附加对象并循环子对象并在需要时更改它的实体状态。保存更改完成后,不会更新任何内容。:

ctx.Reviews.Attach(review);
foreach (ReviewNote item in review.ReviewNotes)
{
   if (item.ReviewNoteID == 0)
   {
       ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Added);
   }
   else
   {
       key = ctx.CreateEntityKey("ReviewNotes", item);
       if (ctx.TryGetObjectByKey(key, out original))
       {
           ctx.ApplyCurrentValues<ReviewNote>(key.EntitySetName, item);
       }

   }
 }

ctx.ObjectStateManager.ChangeObjectState(review, EntityState.Modified);
ctx.SaveChanges();

【问题讨论】:

    标签: entity-framework c#-4.0


    【解决方案1】:

    因为您以MergeOption.NoTracking 开头,所以您的实体是Detached。然后你修改并附加它们。您应该知道 Attach 会导致 Unchanged EntityState ——也就是说,它自从依附于上下文。这样原始值和当前值都具有相同的值集:修改后的值。 这就是为什么调用 SaveChanges 方法后它不会更新的原因。

    我想你也误解了ApplyCurrentValues方法的目的:
    它将获取提供的分离实体的值并使用其 EntityKey 在上下文中定位相同的实体。然后它将用分离实体的属性值替换附加实体的当前标量值。
    在您已经附加了分离实体的情况下,您实际上不需要调用它,而是需要将 ReviewNote 实体的状态更改为 Modified 以便 EF 执行适当的更新针对您的数据存储的方法:

    ctx.Reviews.Attach(review);
    foreach (ReviewNote item in review.ReviewNotes) {
       if (item.ReviewNoteID == 0) {
           ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Added);
       }
       else {
           key = ctx.CreateEntityKey("ReviewNotes", item);
           if (ctx.TryGetObjectByKey(key, out original)) {
               // ctx.ApplyCurrentValues(key.EntitySetName, item);
               ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
           }
       }
     }
    ctx.ObjectStateManager.ChangeObjectState(review, EntityState.Modified);
    ctx.SaveChanges();
    


    编辑: 另一种方法是运行相同的查询并将 ReviewNote 对象放入内存,然后对它们中的每一个调用 ApplyCurrentValues,以便只有具有更改属性的对象才会进入 Modified 状态:

    // This time you don't need to attach:
    //ctx.Reviews.Attach(review);
    // Make sure you have them in the memory:
    Review review2 = (from r in ctx.Reviews.Include("ReviewNotes")
                     where r.ReviewID == reviewID
                     select r).First();
    foreach (ReviewNote item in review.ReviewNotes) {
       if (item.ReviewNoteID == 0) {
           ctx.ReviewNotes.AddObject(item);
       }
       else {
           key = ctx.CreateEntityKey("ReviewNotes", item);
           if (ctx.TryGetObjectByKey(key, out original)) {
               // Note that the item is a detached object now: 
               ctx.ApplyCurrentValues(key.EntitySetName, item);
           }
       }
     }
    ctx.ObjectStateManager.ChangeObjectState(review, EntityState.Modified);
    ctx.SaveChanges();
    

    另请注意,ApplyCurrentValues 仅适用于单个实体上的 标量属性,并且不会考虑导航属性,否则我们只会调用它一次无需进入循环即可将其应用到每个 ReviewNote 上即可查看对象。

    【讨论】:

    • 哇,它不可能那么简单,哈哈,除了那个,我什么都试过了。我早上试试看:)
    • 好的,我试过你的答案,但它不起作用。实际上,该行已更新到数据库,但其他没有对其进行更改的行也是如此。我有 5 条评论存在,所有 5 条都更新了,而不仅仅是实际修改的行。你有什么其他的想法可以分享吗?
    • 是的,这是可以预料的。因为我们只是将每条评论的状态更改为已修改,无论它们是否实际发生了变化。我发布了第二个代码,这次我们不附加修改的对象,而是让它们将修改后的值应用到内存中附加的相关对象。
    • 感谢您的回复。在您的第二个答案中,它不适用于添加新的 ReviewNote。抛出异常“ObjectStateManager 不包含引用对象的 ObjectStateEntry”。当未附加 Review 实体时,似乎会这样做。从根本上来说,更新分离实体的最佳方式是什么?
    • 抱歉,我忘了更改添加新对象部分。正如您在上面更新的代码中看到的那样,现在我们有一个分离的新对象并希望将其添加到数据库中,我们只需调用 ObjectSet.AddObject(entity) 以便实体将获得自动生成的临时 EntityKey 及其 EntityState将设置为已添加。
    猜你喜欢
    • 2015-11-04
    • 2014-10-14
    • 2011-05-26
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 2023-04-09
    • 2016-07-04
    • 1970-01-01
    相关资源
    最近更新 更多