LINQ 和 EF 慢

LINQ and EF Slow

我在一个项目中使用带 Entity Framework 5 的 MVC4。我们有一个名为 MAIN_TABLE 的主 table 和几个子 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;
    }
}

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

首先,如评论中所述,使用 SQL 探查器查看生成的查询执行速度。

还要注意返回记录的数量。暂时 switching automatic changes detection off for the queries returning particularly large result set can give 大幅提升性能。

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

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 将从数据库中 returned。然后在代码中完成所有其他过滤。这是性能不佳的原因之一 - 在数据库上过滤几乎总是比 return 一切都好。

另请注意,您对结果进行了多次排序。首先在设置indlist时,然后在objSelectedDataCollection.DataCollection的每次迭代中设置一次。这是不必要的,在完成过滤之前根本不应该进行排序。也许在你的 foreach 循环之后你可以有一行 indlist = indlist.OrderBy(x => 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 来查询数据库(搜索术语 "deferred execution")值得一读。它只会在您尝试使用结果时发生 - 例如 ToList()ToArray()SingleOrDefault() 和其他各种东西。 context.TableName.Where(some lambda expression); 行此时不会导致数据库查询。您可以在不调用数据库的情况下使用 Where 继续过滤,这就是我发布的代码中发生的情况。 SQL 只会在 indlist = indlist.OrderBy(il => il.Indman_ID).ToList();

行生成并查询 DB

问题出在我写的查询中。

请检查下面的查询。现在我使用 'join' 而不是在子表中查询。

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

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 来检查数据库的执行情况。然后我意识到命中数据库的真正问题。