当我从未将实体添加到上下文时,实体框架错误地保存了它

Posted

技术标签:

【中文标题】当我从未将实体添加到上下文时,实体框架错误地保存了它【英文标题】:Entity Framework incorrectly saving an entity when I never added it to the Context 【发布时间】:2010-12-16 04:45:23 【问题描述】:

我在 .NET 4.0 中使用实体框架。我有一个Subscription 实体,它有许多CFile 实体。我创建了一个新的CFile 实体,但从未实际调用过AddObject。后来我尝试保存与之相关的Subscription 实体,EF 尝试保存CFile 实例,这是我从未想过的!

简化代码:

var subscription = new Subscription();
Context.Subscription.AddObject(subscription);
Context.SaveChanges();

var cfile = new CFile()  Subscription = subscription ;
if (SomeChecksPass(cfile))

    Context.CFiles.AddObject(cfile);

else

    // No AddObject!


subscription.SomeProperty = "changed";
Context.SaveChanges(); // Saves cfile as well, even if I don't want to!

我明白为什么会发生这种情况,但我如何才能这样做呢?这会产生一个相当微妙和晦涩的错误(实际代码显然要复杂得多)。我希望它只保存我明确传递给AddObject 的实体。

我还知道一种解决方法:在我不想保存的实体上调用 Detach。但是,这不是一个好的解决方法,因为我必须确保在未保存 cfile 的每个可能的代码路径中调用 Detach(并且在某些代码路径中它被保存),因此必须在该决定之后调用它已制作,但在保存其他任何内容之前。这很脆弱,我真的不想依赖它。

编辑: cfile 被创建是因为我大部分确实想保存它,但如果某些验证失败或发生错误,那么我不会.不过,我仍然想保存对 subscription 对象的一些更改。

【问题讨论】:

您是否有机会使用自我跟踪实体?还是您使用的是开箱即​​用(代码生成)EF4?我猜你正在使用 STE,并且由于你更新了一个 CFile(被跟踪),EF 正在将它添加到图表中。尝试将 new CFile() 移到您的 using 语句之外(如果您使用的话)。 不,它们不是自我跟踪实体,我没有使用using 声明。 (我会在 using 中包含哪个对象?我还是 EF 的新手。) 通常你会将上下文对象包含在 using 语句中。 我意识到这是一个简化的示例,但是有关您通过创建 cfile 尝试实现的目标的更多信息可能会有所帮助。只是想知道,如果您不将对象保存在数据库中,为什么要创建对象(作为实体图的一部分)。至于 EF 行为,这正是人们在这种情况下所期望的,如果它的行为不同,我会感到惊讶。也就是说,您确实需要破解或解决方法来诱导特定行为。一种方法是 Detach 对象,另一种可能是更改 ObjectStateManager 中的状态。 如果您在创建CFile 的地方调用Detach,然后它只会保存在您当前有代码保存它的地方,怎么样? 【参考方案1】:

尝试在您的 EDMX 中公开外键(如果还没有的话)并执行以下操作:-

var cfile = new CFile()  SubscriptionId = subscription.Id ;

这应该足以防止对象图的形成。

您可能需要在创建订阅对象后调用 Refresh(取决于您的 ID 是如何生成的 - 大多数将在您 SaveChanges() 时自动更新)。

【讨论】:

【参考方案2】:

您不能直接控制要在 Entity Framework 中保存哪些实体,它会保存它认为应该保存的内容,它与关联的对象图一起使用。您可以使用 ObjectStateManager 在 ObjectContext.SaveChanges 覆盖中添加一些逻辑,但这很丑陋。

如果您需要更多地控制要保存的内容和时间,也许最好使用其他模式,例如 Repository | ActiveRecord,您可以更好地控制单独的实体。或者您会经常发现自己处于您所描述的情况。

【讨论】:

【参考方案3】:

我在使用时遇到了同样的问题:

List<Ey> eys = dc.Context.Ey.ToList();
eys = eys .Where(h => h.Employee == myEmployee).ToList();
Ey esh;
for (int k = 1; k < 5; k++)

EmployeeSkillHistory duplicate = histories.Where(h => h.Sl == esh.Sl).FirstOrDefault();
   if (duplicate == null)
       dc.Context.Ey.AddObject(esh);  else  dc.Context.Ey.DeleteObject(esh); 
dc.Context.SaveChanges();

使用前

else  dc.Context.Ey.DeleteObject(esh); 

副本已保存在我的数据库中

【讨论】:

以上是关于当我从未将实体添加到上下文时,实体框架错误地保存了它的主要内容,如果未能解决你的问题,请参考以下文章

尝试添加项目时在实体框架中出现错误

添加新导航属性后,实体框架在查询时尝试将属性设置为 null

EF6基础系列(九)--- 附加离线实体图集到上下文

实体框架分离实体和相关实体消失

代码优先实体框架。急切加载,验证然后保存导致错误

EF6基础系列(十)---离线场景保存实体和实体图集