Entity Framework 6 急切加载具有许多子项和子项的大型对象

Entity Framework 6 Eager loading large object with many children and subchildren

我正在将项目从延迟加载迁移到预加载。

我需要获取一个包含所有子对象的大对象,将其序列化并发送到前端的 SPA 应用程序。

我构建了海量查询,但它占用了太多内存(如 VS 的数据诊断工具所示,内存使用量增加了 200mb,对象本身在序列化为 JSON 时约为 60KB)

Include(x => x.Privileges)
.Include(x => x.Team.Branch)
.Include(x => x.Equality)
.Include(x => x.AddressDetails).Include(x => x.ContactDetails)

.Include(x => x.EmployeeDetails.GeneralUnavailability)
.Include(x => x.EmployeeDetails.EligibilityDocuments.Select(z => z.Document.Priviliges))
.Include(x => x.EmployeeDetails.Position.PositionPayRuleExceptions).Include(x => x.EmployeeDetails.Position.PositionPayRuleExceptions.Select(z => z.PayRule)).Include(x => x.EmployeeDetails.Position.DefaultOffer)


.Include(x => x.TransportationMode.ProofOfLicence.Priviliges).Include(x => x.TransportationMode.ProofOfInsurance)

.Include(x => x.UserAcademicQualifications.Select(z => z.AcademicQualification)).Include(x => x.UserAcademicQualifications.Select(z => z.AcademicQualificationScan.Priviliges))

.Include(x => x.ProfessionalRegistrations.Select(z => z.Body)).Include(x => x.ProfessionalRegistrations.Select(z => z.ProfessionalRegistrationScan.Priviliges))
.Include(x => x.Unavailabilities.Select(z => z.AbsenceType)).Include(x => x.Unavailabilities.Select(z => z.Document.Priviliges))
.Where(x=>x.Id=1)

我不熟悉使用 ef6 进行查询...因为直到现在我都在使用延迟加载。有没有办法优化它?

权限是权限模型列表

Document、ProfessionalRegistrationScan、AcademicQualificationScan、ProofOfInsurance、ProofOfLicence 正在使用具有多对多关系权限的文件模型。

感谢您的帮助

更新

尝试使用 .AsNoTracking() 作为测试..内存使用量随之跃升了 ~100mb。 这对于 ~60kb 的对象来说还是有点高...

嗯,数据就是数据。如果您实际上 需要所有这些,那么这很可能是您能做的最好的。显然,像您所做的那样,急切加载和使用 AsNoTracking 会有所帮助。真的只有几样东西可以看:

  1. 如果您可以 "page",使用 SkipTake 可以减少您必须一次性检索的数据量。显然,您将在分页期间发出更多查询,但每个单独的查询都会更轻松,至少只根据用户的请求发生,而不是总是发生。例如,如果他们只关心结果的第一页,那么您永远不必获取所有其余数据。

  2. 明智地使用 Select。如果您只需要某些列,则仅将这些列选择到其他 DTO class 或异常对象中将减轻查询的需求。

  3. 创建存储过程。如果您要查看 EF 生成的类似这样的查询,它可能会非常庞大​​。 EF 在优化查询方面做得相对较好,但由于连接如此之多,它的体积还是很大。 SQL 服务器必须解析所有这些 SQL 并提出一个执行计划,然后才能真正 运行 查询和 return 结果。存储过程消除了所有这些初始工作。

  4. 如果您不能真正进一步优化它并且您一次需要所有这些数据,那么您唯一的选择就是将资源投入您的数据库。确保它有足够的 RAM、足够的内核、快速的驱动器——最好是 SSD 等。还要确保你的网络服务器和数据库服务器之间的网络尽可能快。理想情况下,它们应该在同一个 LAN 上。如果您必须跨越防火墙等,那将大大降低速度。

所以..我发现的唯一优化将跟踪实体是

app = db.Users.Include(x => x.Privileges)
     .Include(x => x.Team.Branch)
     .Include(x => x.Equality)
     .Include(x => x.AddressDetails)
     .Include(x => x.ContactDetails).FirstOrDefault(x=>x.Id==3);

app = db.Users.Include(x => x.ApplicantDetails.ApplicantOffers.Select(z => z.Offer))
        .Include(x => x.ApplicantDetails.ApplicantOffers.Select(z => z.OfferDocument))
        .Include(x =>x.ApplicantDetails.AppliedPosition.PositionPayRuleExceptions)
       .Include(x => x.ApplicantDetails.AppliedPosition.DefaultOffer)
       .Include(x => x.ApplicantDetails.Branch.AddressDetails).FirstOrDefault(x=>x.Id==3);

基本上,把它分成块。它仍然比延迟加载查询少。

更新: 同样的事情适用于 ef.core 。把它分成块

这是正确的答案,使用 Split 以避免所有那些导致代码 运行 变慢的连接,代价是向 DB 发送更多查询,但每次请求的数据更少。

这样,如果您使用异步编程,所有实体都将被填充,而用户不会注意到延迟。

使用已接受的答案结果,仅此:

app = db.Users.Include(x => x.Privileges)
 .Include(x => x.Team.Branch)
 .Include(x => x.Equality)
 .Include(x => x.AddressDetails)
 .Include(x => x.ContactDetails).FirstOrDefault(x=>x.Id==3)
 .Include(x => x.ApplicantDetails.ApplicantOffers.Select(z => z.Offer))
 .Include(x => x.ApplicantDetails.ApplicantOffers.Select(z => z.OfferDocument))
 .Include(x =>x.ApplicantDetails.AppliedPosition.PositionPayRuleExceptions)
 .Include(x => x.ApplicantDetails.AppliedPosition.DefaultOffer)
 .Include(x => x.ApplicantDetails.Branch.AddressDetails)
 .AsSplitQuery()
 .FirstOrDefaultAsync(x=>x.Id==3);