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
会有所帮助。真的只有几样东西可以看:
如果您可以 "page",使用 Skip
和 Take
可以减少您必须一次性检索的数据量。显然,您将在分页期间发出更多查询,但每个单独的查询都会更轻松,至少只根据用户的请求发生,而不是总是发生。例如,如果他们只关心结果的第一页,那么您永远不必获取所有其余数据。
明智地使用 Select
。如果您只需要某些列,则仅将这些列选择到其他 DTO class 或异常对象中将减轻查询的需求。
创建存储过程。如果您要查看 EF 生成的类似这样的查询,它可能会非常庞大。 EF 在优化查询方面做得相对较好,但由于连接如此之多,它的体积还是很大。 SQL 服务器必须解析所有这些 SQL 并提出一个执行计划,然后才能真正 运行 查询和 return 结果。存储过程消除了所有这些初始工作。
如果您不能真正进一步优化它并且您一次需要所有这些数据,那么您唯一的选择就是将资源投入您的数据库。确保它有足够的 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);
我正在将项目从延迟加载迁移到预加载。
我需要获取一个包含所有子对象的大对象,将其序列化并发送到前端的 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
会有所帮助。真的只有几样东西可以看:
如果您可以 "page",使用
Skip
和Take
可以减少您必须一次性检索的数据量。显然,您将在分页期间发出更多查询,但每个单独的查询都会更轻松,至少只根据用户的请求发生,而不是总是发生。例如,如果他们只关心结果的第一页,那么您永远不必获取所有其余数据。明智地使用
Select
。如果您只需要某些列,则仅将这些列选择到其他 DTO class 或异常对象中将减轻查询的需求。创建存储过程。如果您要查看 EF 生成的类似这样的查询,它可能会非常庞大。 EF 在优化查询方面做得相对较好,但由于连接如此之多,它的体积还是很大。 SQL 服务器必须解析所有这些 SQL 并提出一个执行计划,然后才能真正 运行 查询和 return 结果。存储过程消除了所有这些初始工作。
如果您不能真正进一步优化它并且您一次需要所有这些数据,那么您唯一的选择就是将资源投入您的数据库。确保它有足够的 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);