【问题标题】:Entity Framework 6 Query Performance (M-M Relationship)Entity Framework 6 查询性能(M-M 关系)
【发布时间】:2016-12-07 15:00:42
【问题描述】:

我需要使用 EF 从多个表中查询多个列。当我在选择查询中不包含 M-M 关系时,一切顺利且性能良好。

使用 M-M 关系查询:

result = (from s in db.Member
                      .Include(i => i.Category)
                      .Include(i => i.MemberWorkEntity)
                      .Include(i => i.Status)
                      .Include(i => i.DiscountMethod)
                      .Where(i => i.C_deleted == null)
      select new MemberDTO
      {
          memberNumber = s.memberNumber,
          name = s.name,
          status = s.Status.name,
          email = s.email,
          phone = s.phone,
          mobile = s.mobile,
          fax = s.fax,
          workEntity = (from e in db.WorkEntity.Where(i => i.workEntityLevelID == 2)
                        join sc in s.MemberWorkEntity on e.workEntityID equals sc.workEntityID
                        select e.name).FirstOrDefault(),
          category = s.Category.name,
          discountMethod = s.DiscountMethod.name,
          delegate = s.delegate ? "Yes" : "No",
          leader = s.leader ? "Yes" : "No"

      }).AsNoTracking().ToList<MemberDTO>();

30000条记录执行时间(毫秒):

|1st Execution: 1376
|2nd Execution: 160
|3rd Execution: 145

没有 M-M 关系的查询:

result = (from s in db.Member
                      .Include(i => i.Category)
                      .Include(i => i.MemberWorkEntity)
                      .Include(i => i.Status)
                      .Include(i => i.DiscountMethod)
                      .Where(i => i.C_deleted == null)
      select new MemberDTO
      {
          memberNumber = s.memberNumber,
          name = s.name,
          status = s.Status.name,
          email = s.email,
          phone = s.phone,
          mobile = s.mobile,
          fax = s.fax,
          //removed M-M Relationship Query
          category = s.Category.name,
          discountMethod = s.DiscountMethod.name,
          delegate = s.delegate ? "Yes" : "No",
          leader = s.leader ? "Yes" : "No"

      }).AsNoTracking().ToList<MemberDTO>();

30000条记录执行时间(毫秒):

|1st Execution: 1286
|2nd Execution: 79
|3rd Execution: 67

为什么会有这样的差异(平均慢 2 倍)?如何提高查询性能?

更新:成员和工作实体之间的关系

更新:根据@AndreFilimon 的建议更新了我的查询:

IEnumerable<WorkEntity> workEntities = db.WorkEntity.AsNoTracking().Where(i => i.workEntityLevelID == 2);

result = (from s in db.Member
                      .Include(i => i.Category)                          
                      .Include(i => i.Status)
                      .Include(i => i.DiscountMethod)
                      .Where(i => i.C_deleted == null)
      select new MemberDTO
      {
          memberNumber = s.memberNumber,
          name = s.name,
          status = s.Status.name,
          email = s.email,
          phone = s.phone,
          mobile = s.mobile,
          fax = s.fax,
          workEntity = (from e in workEntities
                        join sc in s.MemberWorkEntity on e.workEntityID equals sc.workEntityID
                        select e.name).FirstOrDefault(),
          category = s.Category.name,
          discountMethod = s.DiscountMethod.name,
          delegate = s.delegate ? "Yes" : "No",
          leader = s.leader ? "Yes" : "No"

      }).AsNoTracking().ToList<MemberDTO>();

30000条记录执行时间(毫秒):

|1st Execution: 1364
|2nd Execution: 122
|3rd Execution: 120

更新:按照@agfc 的建议向我的成员表添加了一个简单的索引:

IEnumerable<WorkEntity> workEntities = db.WorkEntity.AsNoTracking().Where(i => i.workEntityLevelID == 2);

result = (from s in db.Member
                      .Include(i => i.Category)                          
                      .Include(i => i.Status)
                      .Include(i => i.DiscountMethod)
                      .Where(i => i.C_deleted == null)
      select new MemberDTO
      {
          memberNumber = s.memberNumber,
          name = s.name,
          status = s.Status.name,
          email = s.email,
          phone = s.phone,
          mobile = s.mobile,
          fax = s.fax,
          workEntity = (from e in workEntities
                        join sc in s.MemberWorkEntity on e.workEntityID equals sc.workEntityID
                        select e.name).FirstOrDefault(),
          category = s.Category.name,
          discountMethod = s.DiscountMethod.name,
          delegate = s.delegate ? "Yes" : "No",
          leader = s.leader ? "Yes" : "No"

      }).AsNoTracking().ToList<MemberDTO>();

30000条记录执行时间(毫秒):

|1st Execution: 1544
|2nd Execution: 109
|3rd Execution: 105

更新:根据@Klinger 的回答更改了查询:

result = db.MemberWorkEntity.Where(mw => mw.WorkEntity.workEntityLevelID == 2 && mw.Member.C_deleted == null)
        .Select(s => new MemberDTO 
        {
            memberNumber = mw.Member.memberNumber,
            name = mw.Member.name,
            status = mw.Member.Status.name,
            email = mw.Member.email,
            phone = mw.Member.phone,
            mobile = mw.Member.mobile,
            fax = mw.Member.fax,
            workEntity = mw.WorkEntity.name,
            category = mw.Member.Category.name,
            discountMethod = mw.Member.DiscountMethod.name,
            @delegate = mw.Member.@delegate ? "Yes" : "No",
            leader = mw.Member.leader ? "Yes" : "No"
        }).ToList();

30000条记录执行时间(毫秒):

|1st Execution: 1427
|2nd Execution: 80
|3rd Execution: 76

【问题讨论】:

  • 您在投影中包含一个带有连接的子查询,它自然会变慢
  • 您没有在 Member.MemberWorkEntity 和 WorkEntity 之间直接导航? (你能添加一个吗?),你应该避免在选择中运行那个连接查询,因为它会为每个选定的项目循环运行。
  • @Tuco 自然会变慢,我的问题是我可以对查询进行哪些更改以使其更快。
  • 我会问和@AndreiFilimon 一样的问题
  • 子查询是主要的性能消耗。尝试重构您的查询以使用应该在 SQL 服务器(您是否使用 SQL 服务)端生成交叉应用的 GroupJoin,这会更快。另外,你在数据库上有所有正确的索引吗?

标签: c# sql-server linq entity-framework-6


【解决方案1】:

在不查看实体的确切形状的情况下,应该执行以下操作:

result = db.MemberWorkEntity.Where(mw => mw.WorkEntity.workEntityLevelID == 2 && mw.Member.C_deleted == null)
        .Select(s => new MemberDTO 
{
  memberNumber = mw.Member.memberNumber,
  name = mw.Member.name,
  status = mw.Member.Status.name,
  email = mw.Member.email,
  phone = mw.Member.phone,
  mobile = mw.Member.mobile,
  fax = mw.Member.fax,
  workEntity = mw.WorkEntity.name,
  category = mw.Member.Category.name,
  discountMethod = mw.Member.DiscountMethod.name,
  @delegate = mw.Member.@delegate ? "Yes" : "No",
  leader = mw.Member.leader ? "Yes" : "No"
}).ToList();

无需使用Include,因为您投影的是 DTO 而不是实体。 DTO 将没有您所包含的实体的导航属性。 Include 用于在返回实体时进行预加载。

【讨论】:

  • @kilinger 感谢您的回答。我测试了您的解决方案,但结果有所增加:第一次执行:1551 毫秒,第二次执行:175 毫秒,第三次执行:172。
  • @Ricky 只是让我知道要寻找什么,哪个是正确的,有或没有收集?第一次或第二次查询。
  • 正确的是有收藏的那个。我必须进行一些更改才能返回正确的结果:第一个:s.MemberWorkEntity.WorkEntity.workEntityLevelID == 2 // 第二个:workEntity = s.MemberWorkEntity.FirstOrDefault(mw => mw.workEntityLevelID = 2 && mw .memberNumber == s.memberNumber).WorkEntity.name
  • @Ricky 现在怎么样?
  • @Ricky 需要注意的是,此查询仅返回与 MemberWorkEntity 上的行相关的成员
【解决方案2】:

尝试将包含移动到 where 子句之后,您还应该在此处获得一些整体性能: 还将内部查询移出主查询以避免连接,如下所示:

var workEntities=(from e in db.WorkEntity).Where(e=>e.workEntityLevelID==2).ToList();
var result = (from s in db.Member.Where(i => i.C_deleted == null).Include(i => i.Category).Include(i => i.MemberWorkEntity).Include(i => i.Status).Include(i => i.DiscountMethod)
          select new MemberDTO
          {
              memberNumber = s.memberNumber,
              name = s.name,
              status = s.Status.name,
              email = s.email,
              phone = s.phone,
              mobile = s.mobile,
              fax = s.fax,
              workEntity = workEntities.Where(e=>e.workEntityID ==sc.workEntityID).DefaultIfEmpty().Select(e=>e.name).FirstOrDefault(),
              category = s.Category.name,
              discountMethod = s.DiscountMethod.name,
              delegate = s.delegate ? "Yes" : "No",
              leader = s.leader ? "Yes" : "No"

          }).AsNoTracking().ToList<MemberDTO>();

【讨论】:

  • 我根据您的建议修改了查询,性能略有提高但并不多:第一次执行:1478ms,第二次执行:147ms,第三次执行:122。我还能尝试什么?
  • 发布模型,以便我可以看到它们之间的导航是如何定义的,如果模型具有正确定义的导航属性,则不需要该子查询。
  • 我将 workEntities 查询放在外面并将结果查询更新为:(从 e in workEntities join sc in s.MemberWorkEntity on e.workEntityID equals sc.workEntityID select e.name).FirstOrDefault(),但现在我收到以下错误:“无法创建类型为 'Domain.Model.WorkEntity' 的常量值。在此上下文中仅支持原始类型或枚举类型。”
  • 没关系,我的查询工作正常。我已经用你的解决方案更新了我的问题。
  • 这两个建议都没有意义。 1. 不能在 EF 查询中使用本地对象序列 (workEntities)。 2.Includes的位置没有任何区别。请在发布之前尝试您的解决方案。这只会欺骗未来的读者。我建议删除。
猜你喜欢
  • 2020-05-07
  • 2011-07-26
  • 1970-01-01
  • 1970-01-01
  • 2020-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多