LINQ 和 EF 慢
LINQ and EF Slow
我在一个项目中使用带 Entity Framework 5 的 MVC4。我们有一个名为 MAIN_TABLE
的主 table 和几个子 table(即:CHILD_TABLE1
、CHILD_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 来检查数据库的执行情况。然后我意识到命中数据库的真正问题。
我在一个项目中使用带 Entity Framework 5 的 MVC4。我们有一个名为 MAIN_TABLE
的主 table 和几个子 table(即:CHILD_TABLE1
、CHILD_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();
问题出在我写的查询中。
请检查下面的查询。现在我使用 '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 来检查数据库的执行情况。然后我意识到命中数据库的真正问题。