【问题标题】:LINQ and EF SlowLINQ 和 EF 慢
【发布时间】:2015-04-19 13:38:53
【问题描述】:

我在一个项目中使用 MVC4 和 Entity Framework 5。我们有一个名为MAIN_TABLE 的主表,并且有几个子表(即:CHILD_TABLE1CHILD_TABLE2 等)

由于这些不同的子表中的过滤选项很少,我们面临着 LINQ 查询执行速度的问题。

我必须编写一个查询来使用 EF5 从模型中过滤数据。 现在我们一次基于单个过滤器进行编码。即我们正在检查每个过滤的列并触发查询。但它太慢了。还有其他选择吗?

string[] strValue = filter_Values;
foreach (SelectedData selectedData in objSelectedDataCollection.DataCollection)
{
    switch (selectedData.ColumnName) // This is the column name in the GridView defined
    {
        case "Outlook":
            indlist = from jd in indlist 
                      where jd.IND_APP_PASS_STATUS.Any(
                                                  ob => strValue.Contains(ob.Outlook))
                      orderby jd.Indman_ID
                      select jd;
            break;
        case "RS_TP":
            indlist = from jd in indlist
                      where jd.IND_APP_PASS_STATUS.Any(
                                                  ob => strValue.Contains(ob.RS_TP))
                      orderby jd.Indman_ID 
                      select jd;
            break;
        case "Code":
            indlist = (from jd in indlist from jk in jd.IND_APP_PASS_STATUS where strValue.Contains(jk.Code) select jd).ToList();
            break;
    }
}

【问题讨论】:

  • string.Contains() 实际上不是在您的端执行而不是在 SQL 服务器端执行吗?任何人?如果是这样,这就是为什么它很慢
  • 使用 SQL 分析器查看发送到数据库引擎的查询是什么。如果您需要优化查询,您可能需要手动编写 SQL 而不是使用 EF。
  • 你可能需要一些索引。
  • 对于小型项目,我喜欢 EF 的简单性。当我必须处理更复杂的查询时,我会用存储过程来代替它们。
  • 发布最有用的是最终查询及其执行计划。没有这些,如果不知道表的架构、定义了哪些索引以及它包含多少行,就不可能回答这个问题。您还需要提供使用的映射 - 如果jd 使用继承,则可能会导致最终查询中出现一个或多个连接。我首先要看的是 IND_APP_PASS_STATUS`。如果它没有被索引覆盖,数据库将被强制扫描整个表。

标签: c# linq entity-framework asp.net-mvc-4


【解决方案1】:

EF 性能有两个方面 - 服务器和客户端(您的应用程序)。

首先,正如 cmets 中提到的,使用 SQL 分析器查看生成的查询执行速度。

还要注意返回记录的数量。暂时switching automatic changes detection off 用于返回特别大结果集的查询can give 大大提高了性能。

【讨论】:

    【解决方案2】:

    根据您的评论,初始数据库查询是

    indlist = db.IND_TABLE
                .ToList()
                .Where(x => x.Package_No.Trim() != "")
                .OrderBy(x => Int32.Parse(x.Package_No))
                .Select(x => x)
                .ToList<IND_TABLE>();
    

    第一个.ToList() 表示整个IND_TABLE 将从数据库中返回。然后在代码中完成所有其他过滤。这是性能不佳的原因之一 - 在数据库上进行过滤几乎总是比返回所有内容更好。

    另请注意,您对结果进行了多次排序。首先在设置indlist 时,然后在objSelectedDataCollection.DataCollection 的每次迭代中设置一次。这是不必要的,在完成过滤之前您根本不应该排序。也许在您的foreach 循环之后,您可能会有一行indlist = indlist.OrderBy(x =&gt; x.Indman_ID);

    将所有这些放在一起会得到以下结果。

    var indlist = db.IND_TABLE
                    .Where(x => x.Package_No.Trim() != "");
    
    string[] strValue = filter_Values;
    foreach (SelectedData selectedData in objSelectedDataCollection.DataCollection)
    {
        switch (selectedData.ColumnName)
        {
            case "Outlook":
                indlist = indlist.Where(il => il.IND_APP_PASS_STATUS.Any(iaps => strValue.Contains(iaps.Outlook)));
                break;
            case "RS_TP":
                indlist = indlist.Where(il => il.IND_APP_PASS_STATUS.Any(iaps => strValue.Contains(iaps.RS_TP)));
                break;
            case "Code":
                indlist = indlist.Where(il => il.IND_APP_PASS_STATUS.Any(iaps => strValue.Contains(iaps.Code)));
                break;
        }
    }
    
    indlist = indlist.OrderBy(x => x.Indman_ID).ToList();
    

    在 Entity Framework 创建 SQL 来查询数据库时值得一读(搜索术语“延迟执行”)。只有当您尝试使用结果时才会发生这种情况 - 例如使用ToList()ToArray()SingleOrDefault() 和其他各种东西。 context.TableName.Where(some lambda expression); 行此时不会导致数据库查询。您可以继续使用Where 进行过滤,而无需调用数据库,这就是我发布的代码中发生的情况。只会在indlist = indlist.OrderBy(il =&gt; il.Indman_ID).ToList();行生成SQL并查询DB

    【讨论】:

    • 上面的答案有问题,如果我没有在过滤器部分的末尾(在 Select 案例中)给出 ToList(),上面的函数将返回零元素。
    • 除了在实现查询时要小心(我同意这在使用 EF 时会对性能产生巨大影响),我们还观察到 Any() 运算符有时会生成非常次优的查询(特别是当它所应用的可查询对象很复杂时)。我们发现 Count() 运算符在查询生成方面似乎更加“稳定”,并将我们代码中出现的所有 Any() 转换为 Count()。例如myQueryable.Any(x => p(x)) 变为 myQueryable.Count(x => p(x)) > 0。
    【解决方案3】:

    问题出在我写的查询中。

    请检查下面的查询。现在我使用“加入”而不是在子表中查询。

    注意:答案是如果我们使用子表编写查询,那么每个父行都会命中数据库中的每个子表行。因此,您的代码性能将根据行数降低,即如果子表有 10 行,它将访问数据库 10 次。但是,如果我们现在在两个表之间使用“连接”,那么代码只会在每列中命中一次。

    var indlist = db.IND_TABLE
                .Where(x => x.Package_No.Trim() != "");
    
     string[] strValue = filter_Values;
     foreach (SelectedData selectedData in objSelectedDataCollection.DataCollection)
     {
        switch (selectedData.ColumnName)
        {
            case "Outlook":
                indlist = (from jd in indlist join ipas in dbContext.IND_APP_PASS_STATUS on jd.Indman_ID equals ipas.Indman_ID where (strValue.Contains(ipas.Outlook)) orderby jd.Indman_ID select jd).ToList<IND_TABLE>();
                break;
    
            case "RS_TP":
                indmanlist = (from jd in indmanlist join ipas in dbContext.IND_APP_PASS_STATUS on jd.Indman_ID equals ipas.Indman_ID where (strValue.Contains(ipas.RS_TP)) orderby jd.Indman_ID select jd).ToList<IND_TABLE>();
                break;
    
            case "Code":
                indmanlist = (from jd in indmanlist join ipas in dbContext.IND_APP_PASS_STATUS on jd.Indman_ID equals ipas.Indman_ID where (strValue.Contains(ipas.Code)) orderby jd.Indman_ID select jd).ToList<IND_TABLE>();
                break;
        }
    }
    

    我曾使用 SQL Profiler 检查数据库的执行情况。然后我意识到命中数据库的真正问题。

    【讨论】:

      猜你喜欢
      • 2014-12-31
      • 1970-01-01
      • 2013-12-28
      • 1970-01-01
      • 2021-05-09
      • 2011-03-11
      • 1970-01-01
      • 1970-01-01
      • 2015-09-29
      相关资源
      最近更新 更多