我从实体框架中获得了非常奇怪的行为。我正在编写一个WebApi应用程序,因此我从浏览器中获取的对象将被断开/分离。我得到的数据是事务性的,因此它与数据库中的任何给定表都不匹配。我必须进行一些查找和数据操作才能在数据库上完成实际更新。
我似乎遇到的问题是,在查询数据时,我正在填充Tracked Changes缓存。这对我来说似乎不是问题,因为真正的数据来源应该是数据库。当我最终改变数据并调用SaveChanges时,我得到了约束错误。这是我的步骤。
在查看Ctx.ChangeTracker.Entries()中的数据后,我发现要删除的条目在被删除时被标记为已修改。我解决这个问题的方法是为第3步创建一个新的上下文。它神奇地开始工作。我认为就是这样,但在我的测试用例中,我最后一次从数据库中读取以验证我的事务是否正确写入。我得到了一个应该已经删除的额外行。事实上,当直接检查数据库时。再次上一次读取的新上下文修复了问题。
我只是假设默认缓存设置仅用于跟踪更改而不是加速查询。
如果我在查询中尝试使用AsNoTracking,我也会遇到麻烦,因为如果我尝试删除一行查询,我会收到错误。在我的代码中,我不知道我是否要删除或修改,直到稍后。有没有办法清除缓存,所以我不需要创建新的上下文?
有没有更好的方法来处理这些问题?
编辑:
AsNoTracking将在某种程度上发挥作用。我仍然发现自己实例化了更多的DbContext副本以防止错误。必须按顺序删除多个到一个实体,或触发null外键错误。
var details = oldInvoice.details.ToList();
Context.Entry(oldInvoice).State = EntityState.Unchanged;
Context.Entry(oldInvoice).State = EntityState.Deleted;
details.ForEach(a => Context.Entry(a).State = EntityState.Deleted);
答案 0 :(得分:1)
实体框架提供了一个例外DbUpdateConcurrencyException
,您可以抓住对SaveChanges()
的来电。你可以循环这样的错误:
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency(currentValues, databaseValues,
resolvedValues);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
} while (saveFailed);
另外,您的更新代码听起来也很可疑。通常当您通过WebApi或其他机制将数据传递给客户端时,返回的数据没有跟踪数据,因此您应该检查它是否存在并将其重新附加到上下文并更改其状态在致电EntityState.Modified
之前,如果是SaveChanges()
。