【问题标题】:Entity Framework 6 Eager loading large object with many children and subchildrenEntity Framework 6 Eager loading large object with many children and subchildren
【发布时间】:2017-12-18 03:35:49
【问题描述】:

我正在将一个项目从延迟加载迁移到急切加载。

我需要获取一个包含所有子对象的大对象,将其序列化并发送到前端的 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 进行查询...因为直到现在我都在使用延迟加载。有没有办法优化它?

Privilige 是 Privilige 模型的列表

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

感谢您的帮助

更新

尝试使用 .AsNoTracking() 作为测试..内存使用量随之增加约 100mb。 对于 ~60kb 的对象来说,这仍然有点高......

【问题讨论】:

  • 你使用 .AsNoTracking() 吗?另外,“内存过多”是指对象实际上太大而无法放入内存,或者查询执行速度太慢?如果是这种情况,请记住包含许多包含的查询尤其需要一些时间才能转换为 SQL。
  • 查询执行时间为 200 毫秒,比延迟加载快 10 倍。该对象的大小(以 MB 为单位)并没有那么大。它只是执行该查询需要 200 MB 的内存..如 VS 的数据诊断工具所示。 .AsNoTracking() 会使对象分离吗?我也有需要保存更改的地方......
  • 嗯,我想我以前没见过AsNoTracking 增加 内存使用...如果你尝试修改数据肯定会导致一些问题并在以后保存它,尽管可以解决它们。
  • 是否可以将所有这些包含转换为某种内连接查询来构建大对象?我认为,主内存使用来自 EF 尝试自动生成查询。

标签: c# entity-framework asp.net-mvc-5 eager-loading


【解决方案1】:

嗯,数据是数据的排序。如果您实际上需要所有这些,那么这很可能是您能做的最好的事情。显然,像您所做的那样,急切地加载和使用AsNoTracking 会有所帮助。您可以查看的内容实际上只有几样:

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

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

  3. 创建存储过程。如果您要查看由 EF 生成的此类查询,它可能会非常庞大​​。 EF 在优化查询方面做得比较好,但是有了这么多的连接,它就会变得非常庞大。然后,SQL Server 必须解析所有这些 SQL 并提出执行计划,然后才能实际运行查询并返回结果。存储过程消除了所有这些初始工作。

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

【讨论】:

  • 谢谢。我将研究存储过程选项
【解决方案2】:

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

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 。把它分成块

【讨论】:

    【解决方案3】:

    这是正确的答案,使用拆分是为了避免所有导致代码运行缓慢的联接,代价是向数据库发送更多查询,但每个查询请求的数据更少。

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

    使用接受的答案结果,就这样:

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-18
      相关资源
      最近更新 更多