EF Core 延迟加载极慢

EF Core Lazy loading extremely slow

我做了一个启用懒惰的小测试loading.optionsBuilder.UseLazyLoadingProxies().UseSqlServer(ConnectionString);

(使用 EF Core 2.1.4)

我循环使用和不使用仪器,这是我得到的结果

案例一

var instruments = db.instruments.OrderBy(t=>t.id).Include(t=>t.NavPro1).ThenInclude(t=>t.NavPro2).Take(200);

案例 2

var instruments = db.instruments.OrderBy(t=>t.id).Include(t=>t.NavPro1).ThenInclude(t=>t.NavPro2).Take(200);

然后

   foreach (var i in instruments)
   {
        var props = i.NavPro1;
        foreach (var prop in props)
        {
            sbPrintGreeks.AppendLine(prop.NavPro2.Name + " - " + prop.id + " - " + prop.Value);
        }
   }

在没有延迟加载的情况下需要 7 秒来获取 100k 行

延迟加载需要 160 秒才能获得 3k 行。

如何才能获得不错的性能?

这是一个普遍的问题,称为 N+1 问题。

这里发生的事情是当你使用延迟加载时你有更多的请求。

在您使用 Include 的情况下,您有一个巨大的请求可以为您提供所有数据 - 或者每个 table 可能有一个请求,所以在您的情况下有三个请求。这取决于您的数据的确切结构。

在延迟加载的情况下,您对仪器有一个请求,并且对于每个仪器,您都有另一个 NavPro1 请求。对于每个 NavPro1 元素,您还有另一个 NavPro2 请求。

因此,如果您有 1000 台仪器,并且每台仪器有 10 个 NavPro1,那么您现在有 1 + (1000 * (1 + 10)) = 11001 个请求,而不是最多三个请求。太慢了,期间。

是的,避免延迟加载。期间.

问题是 - 总是,回到曾经构建的每个 ORM - 如果你进行延迟加载,每个引用都是延迟加载。这是 1+ 次往返(每 属性,最少一次)。分开 SQL 执行,分开网络时间。这加起来非常快。

这就是为什么每个 ORM 都支持非延迟加载,在 EF 变体中通过 .Include 语句扩展 SQL,或生成单独的 SQL(ef core,tolist与高效 sql).

建立关系

如果您坚持使用延迟加载 - 正如您的问题所暗示的那样,您不会在代码上撒任何魔法粉来避免延迟加载的隐含负面影响。

现在,除此之外 - 3.0 之前的任何 EF Core 都已损坏。是的,那是 3.0——甚至不是 2.2。看,各个部分有很多问题,包括非常基本的 LINQ。和性能。至少 2.2(希望在一个月内发布)应该可以解决一些问题。在那之前,请尝试使用 .Include 和 AsNoTracking - 因为 IIRC 存在一个性能错误,在加载 20 万行时也可能会影响您。

https://github.com/aspnet/EntityFrameworkCore/issues/12451

这是解决方案

服务 .AddDbContext(b => b.ReplaceService());