1

我一直在调查一个真正奇怪的问题,即流氓查询访问我们的生产数据库并尝试将整个表(复数)加载到内存中而无需请求数据。我们有 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 将尝试从不相关的表开始加载和跟踪,而不应用任何过滤器。

为什么并发异常会导致这种行为?有什么办法可以防止这种情况发生吗?

4

1 回答 1

1

事实证明,这不是 EF Core 的问题,而是 Serilog.Exceptions包的问题。这个 Github 问题为我指明了正确的方向https://github.com/dotnet/efcore/issues/15214

警告:如果您将 EntityFrameworkCore 与Serilog.Exceptions一起使用,则必须添加它,否则在某些情况下,您的整个数据库将被记录!这是因为 Entity Framework Core 中的异常具有链接到其中的整个数据库架构的属性

我必须添加一个额外的包Serilog.Exceptions.EntityFrameworkCore,然后在 Serilog 中配置异常丰富,如下所示:

.Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
    .WithDefaultDestructurers()
    .WithDestructurers(new[] { new DbUpdateExceptionDestructurer() }))

有了该配置,冲突异常不再导致我的整个数据库被序列化。

于 2020-12-28T18:06:09.500 回答