【问题标题】:How to deal with a stale cache in Entity Framework?如何处理实体框架中的陈旧缓存?
【发布时间】:2013-08-23 10:38:56
【问题描述】:

我已经得到了非常奇怪的行为形式的实体框架。我正在编写一个 WebApi 应用程序,因此我从浏览器获取的对象是断开连接/分离的。我返回的数据是事务性的,因此它与数据库中的任何给定表都不匹配。我必须进行大量查找和数据操作才能对数据库进行实际更新。

我似乎遇到的问题是,在查询数据时,我正在填满 Tracked Changes 缓存。这对我来说似乎不是问题,因为真正的数据来源应该是数据库。当我最终更改数据并调用 SaveChanges 时,我得到约束错误。这是我的步骤。

  1. 查询数据。
  2. 创建要插入的行。
  3. 将行与 db 进行比较并进行 db 更改。

查看 Ctx.ChangeTracker.Entries() 中的数据后,我发现一个要删除的条目在应该删除时被标记为已修改。我解决它的方法是为第 3 步创建一个新的上下文。它神奇地开始工作。我以为就是这样,但在我的测试用例中,我最后一次读取数据库以验证我的事务是否正确写入。我得到了一个应该已经被删除的额外行。事实上,当直接检查数据库时。最后一次读取的新上下文再次解决了问题。

我只是假设默认缓存设置仅用于跟踪更改而不是加速查询。

如果我尝试在查询中使用 AsNoTracking,我也会遇到麻烦,因为如果我尝试删除这样查询的行,我会收到错误。在我的代码中,我不知道我是否要在以后删除或修改。有没有办法清除缓存,这样我就不需要创建新的上下文了?

有没有更好的方法来处理这些问题?

编辑:

AsNoTracking 在某种程度上可以解决问题。我仍然发现自己实例化了更多 DbContext 副本以防止错误。必须按顺序删除多对一实体,否则会触发空外键错误。

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);

【问题讨论】:

  • 长时间保留上下文是个坏主意,因为当跟踪图包含大量实体时,它的性能不佳。我的建议是不要在请求之间重复使用您的上下文。这也应该可以解决任何并发更新问题

标签: c# entity-framework asp.net-web-api


【解决方案1】:

Entity Framework 提供了一个异常 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()之前。

【讨论】:

  • 安德鲁感谢您的回答。您对丢失的跟踪数据是正确的。所以我完全按照你在底部描述的做。除了之前的查询弄乱了跟踪数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-13
  • 2011-05-19
  • 1970-01-01
  • 1970-01-01
  • 2014-08-22
  • 2012-05-31
  • 1970-01-01
相关资源
最近更新 更多