任何改进此EF查询的方法

时间:2018-11-29 19:45:06

标签: c# sql-server entity-framework

今天,在第三方使用的WebApi中,当我们调用方法时,我们开始遇到失败。

该方法连接到几个表并将它们联接起来。 EF的错误消息是

  
    

查询处理器用尽了内部资源,无法生成查询计划。这是罕见的事件,仅在极其复杂的查询或引用大量表或分区的查询中才会发生。请简化查询。如果您认为收到此消息是错误的,请联系客户支持服务以获取更多信息。

  
return context.leads
            .Where(q => q.eventID == 1234)
            .Join(context.config,
                leads => leads.configId,
                config => config.configId,
                (leads, config) => new { leads, config })
            .Where(p => keys.Contains(p.leads.leadId))

键是检索到的潜在顾客ID的IEnumerable。基本上,我们提取ID列表,然后执行上述操作,检查键集合,以便我们的查询返回准确的数据。

密钥包含约28k个ID。

请注意,自迁移到Azure云以来,这才刚刚成为一个问题,但这是一个巧合

2 个答案:

答案 0 :(得分:1)

  

键是检索到的潜在顾客ID的IEnumerable。

     

密钥包含约28k个ID。

所以这个:

return context.leads
            .Where(q => q.eventID == 1234)
            .Join(context.config,
                leads => leads.configId,
                config => config.configId,
                (leads, config) => new { leads, config })
            .Where(p => keys.Contains(p.leads.leadId))

会将ID粘贴到SQL查询文本中,例如:

SELECT … WHERE LeadId in (1,23,3,4,5,6,45,34, . . . )

导致大型,不可重用且昂贵的解析和编译查询。最好将ID加载到表中并加入它们,或者使用XML,JSON或表值参数将其传递到服务器。

  

基本上,我们提取ID列表,然后执行上面的操作

那就不要那样做。如果所需的ID列表在数据库中,则将其加入查询中。避免从数据库中读取28K ID,然后将其发送回查询正文中。

答案 1 :(得分:1)

为帮助澄清David的回答:

您用来获取28k ID的查询是什么?那是您应该合并到该查询中的查询。提取ID对于#相对较小的ID(而不是28k)可以正常工作。如果在EF中正确设置了关系,则可以避免显式联接。 EF不仅是SQL的替代包装。尽管您可以使用断开连接的实体之间的显式联接来编写它,但是当设置为ORM时,相关实体之间会相互了解,它的功能要强大得多。您的EF表达式应该更像:

var leadsQuery = context.leads
    .Include(l => l.Config)  
    .Where(l => l.eventID == 1234
      && /* Insert criteria to determine which Leads to return. /*);

要插入的where子句是IEnumerable ID中的 not 。这对于少量的行很好用。相反,这应该是您用来获取这些ID的标准。

线索应具有对Config的引用。无需为每个表声明DbSet,然后告诉EF将它们加入。如果Lead表具有ConfigId,则:

public class Lead
{
   //...
   public virtual Config Config { get; set; }
}

然后,如果这是EF核心,则设置映射:(IEntityTypeConfiguration或OnModelCreating)

builder.Entity<Lead>()
  .HasOne(x => x.Config)
  .WithMany() // Lead has a config, Config does not have a collection of Leads.
  .HasForeignKey("ConfigId"); // Creates a shadow property for the FK.

或EF 6

builder.Entity<Lead>()
  .HasRequired(x => x.Config)
  .WithMany()
  .Map(x => x.MapKey("ConfigId")); // Similar to above. Set up the relationship without a FK in the entity, map directly to the table.

通过使用.Include(l => l.Config),您可以访问lead.Config属性以获取该线索的Config。无需编写查询来分别返回线索和配置以及其他数据。您无需.Include()来查询相关实体,但是,如果要在查询后通过属性访问那些实体,则应使用.Include()以避免额外的延迟加载回调到数据库。 (EF的一项强大功能,但如果使用不当,则价格昂贵。)

相关问题