【发布时间】:2020-12-28 03:58:41
【问题描述】:
我一直在调查一个真正奇怪的问题,即恶意查询访问我们的生产数据库并尝试将整个表(复数)加载到内存中而不请求数据。我们有一些 EF6 的应用程序和一些在 EF Core 上的应用程序,最初的指责是针对 EF Core 版本 2.2.4 中的查询的客户端评估。但是,即使在升级到 EF Core 3.1.7 并禁用客户端评估后,问题仍然存在。 (这是一个使用 Microsoft SqlServer 的 .Net Framework 4.7.2 WebAPI 应用程序)
我能够启用调试日志,发现只要抛出 Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException 就会出现问题。 (由于某种原因,此异常默认情况下仅在调试模式下编写,因此相关性在很长一段时间内都没有被注意到。)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 将尝试从不相关的表开始加载和跟踪,而不应用任何过滤器。
为什么并发异常会导致这种行为?有什么办法可以防止这种情况发生吗?
【问题讨论】:
-
这是哪个数据库提供程序,是否启用了延迟加载,这是哪个确切的 EF 版本?
-
@GertArnold 这是 .Net Framework 4.7.2 上带有 SqlServer 的 EF Core 3.1.7。延迟加载未启用。
-
确实很奇怪!只是一些想法......你能绝对隔离EF中的问题吗?我在想... 1)使用您的 GenerateConflict 方法。 2)在该方法中创建一个新的上下文。 3)确保您在上下文中没有覆盖 SaveChanges,因此它只是 EF 工作。 4) Try/Catch/Swallow SaveChanges() 中的错误,以确保调用堆栈或全局错误处理程序没有做任何奇怪的事情。我想如果您仍然遇到问题并且已经消除了 EF 之外的流氓代码的所有可能性,那么它是 MS 的错误票。
-
@NeilW 在所有 Serilog.Exceptions 之后,它确实是一个单独的包的问题。添加了我最后使用的解决方案,以防它帮助其他人
标签: .net entity-framework entity-framework-core