我一直在调查一个真正奇怪的问题,即流氓查询访问我们的生产数据库并尝试将整个表(复数)加载到内存中而无需请求数据。我们有 EF6 应用程序的一些部分和 EF Core 上的一些应用程序,最初的指责指向 EF Core 版本2.2.4中查询的客户端评估。但是,即使在升级到 EF Core 3.1.7并禁用客户端评估后,问题仍然存在。(这是一个使用 Microsoft SqlServer的 .Net Framework 4.7.2 WebAPI 应用程序)
我能够启用调试日志,发现只要Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException抛出 a 就会出现问题。(由于某种原因,此异常默认情况下仅在调试模式下编写,因此相关性在很长一段时间内都没有被注意到。)这DbUpdateConcurrencyException本身不是问题,但每当它发生时,EF Core 就会开始尝试加载和跟踪所有数据上下文中的表,直到内存不足。在我的情况下,日志如下所示:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:数据库操作预计会影响 1 行,但实际上影响了 0 行。自加载实体以来,数据可能已被修改或删除。
其中一些调试消息。每桌一个Microsoft.EntityFrameworkCore.Query.QueryExecutionPlanned
queryContext => new QueryingEnumerable<MyTable>( (RelationalQueryContext)queryContext, RelationalCommandCache, null, null, Func<QueryContext, DbDataReader, ResultContext, int[], ResultCoordinator, MyTable>, Namespace.MyContext, null )
然后它开始拉取和跟踪数据,直到它达到一个足够大以至于引发 OOM 异常的数据。我能够通过使用这样的代码显式强制冲突来重现该问题
public void GenerateConflict(int id)
{
var element = _context.MyData.FirstOrDefault(x => x.Id == id);
if (element != null)
{
element.DisplayName = "test";
_context.Database.ExecuteSqlRaw($"Delete from MyData where Id={id}");
}
_context.SaveChanges();
}
当像这样手动强制冲突时。我可以看到相同的行为,随后,EF Core 将尝试从不相关的表开始加载和跟踪,而不应用任何过滤器。
为什么并发异常会导致这种行为?有什么办法可以防止这种情况发生吗?