gpt4 book ai didi

c# - IEnumerable 过滤时处理时间太长

转载 作者:行者123 更新时间:2023-11-30 14:06:01 25 4
gpt4 key购买 nike

我有一种感觉,我知道这种行为的原因是什么,但我不知道解决它的最佳方法是什么。

我已经构建了一个 LinqToSQL 查询:

public IEnumerable<AllConditionByCountry> GenerateConditions(int paramCountryId)
{
var AllConditionsByCountry =
(from cd in db.tblConditionDescriptions...
join...
join...
select new AllConditionByCountry
{
CountryID = cd.CountryID,
ConditionDescription = cd.ConditionDescription,
ConditionID = cd.ConditionID,
...
...
}).OrderBy(x => x.CountryID).AsEnumerable<AllConditionByCountry>();

return AllConditionsByCountry;
}

此查询返回大约 9500 多行数据。

我是这样从我的 Controller 调用它的:

svcGenerateConditions generateConditions = new svcGenerateConditions(db);
IEnumerable<AllConditionByCountry> AllConditionsByCountry;
AllConditionsByCountry = generateConditions.GenerateConditions(1);

然后我正在循环:

foreach (var record in AllConditionsByCountry)
{
...
...
...

这是我认为的问题所在:

var rList = AllConditionsByCountry
.Where(x => x.ConditionID == conditionID)
.Select(x => x)
.AsEnumerable();

我正在根据从上述查询中收集的数据进行嵌套循环(利用我从 AllConditionByCountry 获得的原始数据。我认为这就是我的问题所在。当它在做对数据的过滤,它会大大减慢。

基本上这个过程会写出一堆文件(.json、.html)我首先只使用 ADO.Net 对此进行了测试,运行所有这些记录大约需要 4 秒。使用 EF(存储过程或 LinqToSql)需要几分钟。

我应该对我正在使用的列表类型做些什么,或者这只是使用 LinqToSql 的代价吗?

我试过返回 List<AllConditionByCountry> , IQueryable , IEnumerable来 self 的 GenerateConditions方法。列表花了很长时间(类似于我现在看到的)。 IQueryable当我尝试执行第二个过滤器时出现错误(查询结果不能多​​次枚举)。

我在 LinqPad 中运行了同样的 Linq 语句,它在不到一秒的时间内返回。

我很乐意添加任何其他信息。

请告诉我。

编辑:

foreach (var record in AllConditionsByCountry)
{
...
...
...
var rList = AllConditionsByCountry
.Where(x => x.ConditionID == conditionID)
.Select(x => x)
.AsEnumerable();
conditionDescriptionTypeID = item.ConditionDescriptionTypeId;
id = conditionDescriptionTypeID + "_" + count.ToString();
...
...
}

最佳答案

TL;DR:您对数据库进行了 9895 次查询,而不是一次。您需要重写您的查询,以便只执行一个查询。查看 IEnumerable 的工作原理以获得执行此操作的一些提示。

啊,是的,那个for循环是你的问题。

foreach (var record in AllConditionsByCountry)
{
...
...
...
var rList = AllConditionsByCountry.Where(x => x.ConditionID == conditionID).Select(x => x).AsEnumerable();
conditionDescriptionTypeID = item.ConditionDescriptionTypeId;
id = conditionDescriptionTypeID + "_" + count.ToString();
...
...
}

Linq-to-SQL 的工作方式与 Linq 类似,因为它(粗略地说)将函数附加到链中,以便在迭代可枚举时执行 - 例如,

Enumerable.FromResult(1).Select(x => throw new Exception());

这实际上不会导致您的代码崩溃,因为永远不会迭代可枚举对象。 Linq-to-SQL 的运行原理类似。所以,当你定义这个时:

var AllConditionsByCountry =
(from cd in db.tblConditionDescriptions...
join...
join...
select new AllConditionByCountry
{
CountryID = cd.CountryID,
ConditionDescription = cd.ConditionDescription,
ConditionID = cd.ConditionID,
...
...
}).OrderBy(x => x.CountryID).AsEnumerable<AllConditionByCountry>();

您不是对数据库执行任何操作,您只是指示 C# 构建一个在迭代时执行此操作的查询。这就是为什么只声明这个查询很快。

当您进入 for 循环时,您的问题就来了。当您点击 for 循环时,您表示要开始迭代 AllConditionsByCountry迭代器。这会导致 .NET 关闭并执行初始查询,这需要时间。

当您调用 AllConditionsByCountry.Where(x => x.ConditionID == conditionID) 时在 for 循环中,您正在构造另一个实际上不执行任何操作的迭代器。大概您实际上使用了 rList 的结果然而,在该循环中,您实际上是在构建要针对数据库执行的 N 个查询(其中 N 是 AllConditionsByCountry 的大小)。

这会导致您对数据库有效执行大约 9501 次查询 - 1 次用于初始查询,然后一次查询用于原始查询中的每个元素。与 ADO.NET 相比速度急剧下降是因为您可能比最初多进行了 9500 次查询。

理想情况下,您应该更改代码,以便对数据库执行一次且仅一次查询。您有几个选择:

  • 重写 Linq-to-SQL 查询,使所有的腿部工作都由 SQL 数据库完成
  • 重写 Linq-to-SQL 查询,使其看起来像这样

    var conditions = AllConditionsByCountry.ToList();foreach(条件中的var记录){ var rList = conditions.Where(....);

请注意,在该示例中,我正在搜索 conditions而不是 AllConditionsByCountry - .ToList()将返回一个已经迭代过的列表,因此您不会再创建任何数据库查询。这将仍然很慢(因为您在 9500 条记录上执行 O(N^2)),但它仍然比创建 9500 个查询快,因为它将全部在内存中完成。

  • 如果您更习惯使用原始 SQL 而不是 Linq-to-SQL,只需在 ADO.NET 中重写查询即可。这没什么问题。

我想我应该指出哪些方法会导致 IEnumerable 被迭代,哪些不会。

任何名为 As* 的方法(例如 AsEnumerable<T>() )不会导致枚举被迭代。它本质上是一种从一种类型转换为另一种类型的方法。

任何名为 To* 的方法(例如 ToList<T>() )将导致枚举被迭代。如果是 Linq-to-SQL,这也将执行数据库查询。任何也会导致您从可枚举中获取值的方法也会导致迭代。您可以通过创建查询并使用 ToList() 强制迭代来利用它。然后搜索该列表 - 这将导致比较在内存中完成,这就是我上面演示的内容

关于c# - IEnumerable 过滤时处理时间太长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51408399/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com