问题 使用跨多个数据上下文的POCO动态代理跟踪Entity Framework 4.0中的更改


我开始搞乱EF 4.0因为我对POCO的可能性感到好奇......我想模拟断开的web环境并编写以下代码来模拟这个:

  1. 将测试对象保存在数据库中。
  2. 检索测试对象
  3. 处理与我用于检索它的测试对象相关联的DataContext
  4. 更新测试对象
  5. 创建一个新的数据上下文并在测试对象上保留更改,这些更改将在针对我的POCO对象生成的DynamicProxy中自动跟踪。

问题是当我在上面的Test方法中调用dataContext.SaveChanges时,不会应用更新。当我检查其EntityStateTracker时,testStore实体显示状态为“已修改”,但当我在新的dataContext的Stores属性中查看它时,它不再被修改。我原本以为在新的dataContext上调用Attach方法也会使对象的“Modified”状态结束,但看起来并非如此。有什么我想念的吗?我肯定在使用DynamicProxies进行自我跟踪POCO。

private static void SaveTestStore(string storeName = "TestStore")
{
  using (var context = new DataContext())
  {
    Store newStore = context.Stores.CreateObject();
    newStore.Name = storeName;
    context.Stores.AddObject(newStore);
    context.SaveChanges();
  }
}

private static Store GetStore(string storeName = "TestStore")
{
  using (var context = new DataContext())
  {
    return (from store in context.Stores
            where store.Name == storeName
            select store).SingleOrDefault();
  }
}

[Test]
public void Test_Store_Update_Using_Different_DataContext()
{
  SaveTestStore();
  Store testStore = GetStore();
  testStore.Name = "Updated";      

  using (var dataContext = new DataContext())
  {
    dataContext.Stores.Attach(testStore);
    dataContext.SaveChanges(SaveOptions.DetectChangesBeforeSave);        
  }

  Store updatedStore = GetStore("Updated");
  Assert.IsNotNull(updatedStore);
}

2792
2018-04-15 01:17


起源

嘿Rob ...你有没有看到你问题的第二个答案?从那以后没有收到你的消息..我想知道你对它的看法= / - Artiom Chilaru


答案:


如您稍后所述,您使用的是POCO生成器,而不是自跟踪实体生成器。

我也试过了,变得非常困惑。似乎代理类没有按预期工作,并且可能存在错误。然后又来了。 MSDN上没有一个例子尝试这样的东西,当他们在应用程序的不同层引用更新时(我们在这里做的事情),他们使用自我跟踪实体,而不是POCO代理。

我不确定这些代理是如何工作的,但它们确实存在某种状态(我设法在私有属性中找到“已修改”状态)。但似乎这个属性完全被忽略了。将属性附加到上下文时,上下文会向ObjectStateManager添加一个条目,并在其中存储更多状态更新。此时,如果您进行更改 - 它将被注册并应用。

问题是当你.Attach一个实体时 - 来自代理的Modified状态不会转移到上下文中的状态管理器。此外,如果您使用context.Refresh(),更新将被覆盖,并被遗忘!即使你将RefreshMode.ClientWins传递给它。我尝试将对象状态的state属性设置为modified,但无论如何都被覆盖了,原始设置被恢复了。

似乎EF中没有错误,唯一的方法就是使用这样的东西:

using (var db = new Entities())
{
    var newUser = (from u in db.Users
                    where u.Id == user.Id
                    select u).SingleOrDefault();
    db.Users.ApplyCurrentValues(user);
    db.SaveChanges();
}

还有一件事

Entitity Framework:使用POCO方法在SOA中更改跟踪

似乎POCO不支持您正在寻找的方法,并且正如我所预期的那样,自我跟踪实体的创建是为了解决您正在测试的情况,而POCO的代理只跟踪他们创建的上下文中的更改。或者所以它看起来...


8
2018-04-22 06:22



感谢所有的辛勤工作! POCO仅在它们创建的上下文中跟踪的事实真的很不幸。我几乎希望它是一个bug,而不仅仅是它是如何设计的。我试图避免在保存期间从数据库重新加载实体...当然,加载原始和更新实体时,很容易知道是否发生了更改。 - Rob Packwood
+1用于指出POCO模板生成的实体与STE POCO模板生成的实体之间的区别 - kenwarner


尝试

        db.ObjectStateManager.ChangeObjectState(user, System.Data.EntityState.Modified);

在调用SaveChanges之前


5
2018-05-26 00:57



请注意,Mrs Entity Framework本人Julie Lerman也提出了这个解决方案 thedatafarm.com/blog/data-access/... - kenwarner


在玩了自我跟踪实体后,我意识到你的错误是什么。 您应该反而指示您希望数据上下文将您对其所做的新更改应用于数据库,而不是尝试将实体附加到数据上下文。

在这种情况下,将“保存”代码更改为:

using (var dataContext = new DataContext())
{
    dataContext.Stores.ApplyChanges(testStore);
    dataContext.SaveChanges();        
}

至少我已经在我的本地机器上测试了它,并且在这次更新后它运行了:)
希望这可以帮助!


2
2018-04-21 19:59



你是如何得到ApplyChanges()方法出现的?我必须做错事,因为我不存在这种方法...... - Rob Packwood
原来如此。您正在使用自我跟踪实体代码生成模板,而我正在使用POCO模板。通过POCO模板,实体通过DynamicProxy在运行时覆盖POCO对象并通过一些特殊的更改跟踪功能来跟踪自己。在尝试了似乎所有事情之后,我无法在原始问题中使用我的场景。 - Rob Packwood
我最初用POCO实体开始我的测试,但我找不到如何获得对象的“状态”(修改/未更改),而你说它在那里。重读你的帖子后,我看到“我肯定在使用自我跟踪POCO”,所以我尝试了自我跟踪模板......那么你从哪里得到了对象的状态? - Artiom Chilaru


我认为问题的根源是您对Context对象的管理。

使用POCO处理时,上下文不会通知该上下文中的实体它们不再与上下文关联。使用POCO进行的更改跟踪全部由上下文管理,因此您会遇到一些有趣的问题,其中POCO将表现为仍然附加到上下文但实际上它不是并且重新附加到另一个上下文应该引发关于附加的错误到多个上下文。

有一个关于这个的小帖子你可能想在这里阅读: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/5ee5db93-f8f3-44ef-8615-5002949bea71/

如果你切换到自我跟踪,我想你会发现你的实体按照你想要的方式工作。

另一个选项是将属性添加到poco的部分类,以便在从用于加载POCO的上下文中分离POCO后手动跟踪更改。


0
2017-10-14 14:27