我有一种感觉,知道这种行为的原因是什么,但我不知道解决该问题的最佳方法是什么。
我建立了一个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多行数据。
我从控制器中这样调用:
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
,GenerateConditions
。 List花费了很长时间(类似于我现在所看到的)。 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();
...
...
}
答案 0 :(得分:6)
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关闭并执行初始查询,这需要时间。
在for循环中调用AllConditionsByCountry.Where(x => x.ConditionID == conditionID)
时,您正在构造另一个实际上不做任何事情的迭代器。大概您实际上在该循环中使用了rList
的结果,但是,您实际上是在构造要针对数据库执行的N个查询(其中N是AllConditionsByCountry的大小)。
这导致一种情况,在该情况下,您将有效地对数据库执行大约9501个查询-初始查询为1个,然后对原始查询中的每个元素执行一个查询。与ADO.NET相比,速度急剧下降是因为您可能比原来多进行了9500次查询。
理想情况下,您应该更改代码,以便针对数据库执行一个查询,只有一个查询。您有两种选择:
重写Linq-to-SQL查询,使其看起来像这样
var条件= AllConditionsByCountry.ToList(); foreach(条件记录) { var rList = Conditions.Where(....); }
请注意,在该示例中,我搜索的是conditions
而不是AllConditionsByCountry
-.ToList()
将返回已经被迭代的列表,因此您无需再创建任何数据库查询。这仍然会很慢(因为您正在对9500条记录进行O(N ^ 2)条记录),但是由于它全部在内存中完成,因此它仍然比创建9500条查询要快。 >
我想我应该指出导致IEnumerable迭代的方法,而不是导致迭代的方法。
任何名为As*
(例如AsEnumerable<T>()
)的方法都不会 引起可枚举的迭代。从本质上讲,这是一种从一种类型转换为另一种类型的方法。
任何名为To*
(例如ToList<T>()
)的方法都将导致枚举被迭代。如果使用Linq-to-SQL,它还将执行数据库查询。任何还会导致您从可枚举中获取值的方法也会引起迭代。您可以通过创建查询并使用ToList()
强制迭代,然后搜索该列表来发挥自己的优势,这将导致比较在内存中完成,这就是我上面演示的内容
答案 1 :(得分:1)
$(By.xpath("//div[contains(text(), 'My Tasks')]"))
关于这部分:
//Firstly: IEnumerable<> should be List<>, because you need to massage result later
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)
.ToList() //return a list, so only 1 query is executed
//.AsEnumerable<AllConditionByCountry>();//it's useless code, anyway.
return AllConditionsByCountry;
}
顺便说一句,
您应该对结果进行分页,没有页面将需要100多个结果。 10K的回报本身就是问题。
GenerateConditions(int paramCountryId,int page = 0,int pagesize = 50)
很奇怪,您必须使用子查询,通常这意味着GenerateConditions没有返回所需的数据结构,您应该对其进行更改以提供正确的数据,而不再需要子查询