【发布时间】:2020-01-10 05:53:55
【问题描述】:
我正在为我的项目使用 Entity Framework 6(使用生成的模型和 DbContext)。我在我的程序中使用的架构是数据访问层是从应用程序中抽象出来的。这意味着我的实体将大部分处于分离状态。
似乎重新连接任何以前连接的实体似乎是不可能的(或者我没有正确执行)。
我已经单独测试了一个案例,并且能够缩小问题范围。步骤如下。
-
从上下文中获取实体并释放上下文。这会分离实体。
public async Task<ArchiveEntry> GetEntry(string api, string id) { // Omitted some code for simplicity ArchiveEntry entry; using (var db = new TwinTailDb()) { entry = await db.ArchiveEntries.Where(a => a.Id == id).SingleOrDefaultAsync(); } return entry; } -
无论是否更改,都保存实体。
public async Task Save() { // Omitted some code for simplicity using (var db = new TwinTailDb()) { db.ArchiveEntries.AddOrUpdate(a => a.Id, this); await db.SaveChangesAsync(); } } -
最后,重新附加分离的实体。
public async Task RevertChanges() { using (var db = new TwinTailDb()) { if (db.Entry(this).State == EntityState.Detached && Id != 0) { db.ArchiveEntries.Attach(this); //await db.Entry(this).ReloadAsync(); // Commented since this is irrelevant } } } -
然后我运行这个函数来使用上面的代码。
public async Task Test() { ArchiveEntry entry = await ArchiveService.GetEntry(null, "7"); await entry.Save(); await entry.RevertChanges(); }
然后它抛出这个错误:
附加类型为“TwinTail.Entities.ArchiveEntry”的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后将非新实体的状态设置为“未更改”或“已修改”。
这里有一点很重要。如果我跳过第 2 步,它不会引发异常。
我的猜测是实体被修改并保存在不同的上下文中。如果步骤 2 被跳过,实体保持不变,因此重新附加它不会造成问题(只是猜测)。然而,这个实体已经处于分离状态,所以这应该是无关紧要的。
另外一点,ChangeTracker 在这些步骤中不包含任何内容。此外,如果我对分离的实体执行任何上下文操作,它会抛出一个异常,说明它应该首先附加。我还注意到内部的 _entitywrapper 仍然有对旧上下文的引用。
所以,最后,问题来了。如何正确重新附加实体以及为什么会发生此异常。
我在另一个问题 (How to revert changes on detached entities) 中提出了类似的问题,但我觉得我需要发布一个新问题,因为这更笼统。
【问题讨论】:
-
如果你想从上下文 A 加载一个实体,然后释放上下文 A,然后使用上下文 B 附加/保存它,然后释放上下文 B,然后将其重新附加到上下文 C 以便从数据库,你做错了。
-
您需要代理实体吗?如果不这样做,您可以禁用代理,实体将停止携带有关其原始
DbContext的信息。 -
@danludwig 哦等等。 “使用”声明还不够吗?我认为它会在 using 块之后自动安全地处理上下文。如果是,那么我做错了一切哈哈。对于每个数据库操作,我都会创建一个新上下文并在函数结束之前(使用 using)处理它。我想我记得读过一篇文章说应该是这样的。但请赐教。
-
@SteveRuble 好的,我会试试的。
标签: c# .net entity-framework