![](/img/trans.png)
[英]WCF ServiceHost crashes when UserNamePasswordValidator takes too long to process
[英]IEnumerable takes too long to process when filtering on it
我有一種感覺我知道這種行為的原因是什么,但我不知道解決它的最佳方法是什么。
我已經構建了一個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的價格?
我試圖從我的GenerateConditions
方法返回List<AllConditionByCountry>
, IQueryable
, IEnumerable
。 列表花了很長時間(類似於我現在看到的)。 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循環時,表示你想要開始迭代AllConditionsByCountry
迭代器。 這會導致.NET關閉並執行初始查詢,這需要時間。
當您在for循環中調用AllConditionsByCountry.Where(x => x.ConditionID == conditionID)
時,您正在構建另一個實際上不執行任何操作的迭代器。 據推測,您實際上在該循環中使用了rList
的結果,但是,您實際上構建了針對數據庫執行的N個查詢(其中N是AllConditionsByCountry的大小)。
這導致您有效地對數據庫執行大約9501次查詢的情況 - 1用於初始查詢,然后對原始查詢中的每個元素執行一次查詢。 與ADO.NET相比,大幅放緩是因為您可能比原來多了9500個查詢。
理想情況下,您應該更改代碼,以便對數據庫執行一個且僅執行一個查詢。 你有幾個選擇:
重寫Linq-to-SQL查詢,使其看起來像這樣
var conditions = AllConditionsByCountry.ToList(); foreach(條件中的var記錄){var rList = conditions.Where(....); }
請注意,在該示例中,我搜索conditions
而不是AllConditionsByCountry
- .ToList()
將返回已經迭代的列表,因此您不再創建數據庫查詢。 這仍然會很慢(因為你正在做超過9500條記錄的O(N ^ 2)),但它仍然比創建9500查詢更快,因為它將全部在內存中完成。
我想我應該指出哪些方法會導致IEnumerable被迭代而哪些方法沒有。
任何名為As*
方法(例如AsEnumerable<T>()
)都不會導致枚舉被迭代。 它本質上是一種從一種類型轉換為另一種類型的方式。
任何名為To*
方法(例如ToList<T>()
)都將導致重復枚舉。 在Linq-to-SQL的情況下,這也將執行數據庫查詢。 任何導致您從可枚舉中獲取值的方法也會導致迭代。 您可以通過創建查詢並使用ToList()
強制迭代然后搜索該列表來使用此優勢 - 這將導致比較在內存中完成,這是我在上面演示的內容
//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;
}
關於這部分:
foreach (var record in AllConditionsByCountry) // you can use AllConditionsByCountry.ForEach(record=>{...});
{
...
//AllConditionsByCountry will not query db again, because it's a list, no long a query
var rList = AllConditionsByCountry.Where(x => x.ConditionID == conditionID);//.Select(x => x).AsEnumerable(); //no necessary to use AsXXX if compilation do not require it.
...
}
順便說一句,
你應該把你的結果分頁,沒有頁面需要100+結果。 10K回報就是問題本身。
GenerateConditions(int paramCountryId,int page = 0,int pagesize = 50)
你必須使用一個子查詢很奇怪,通常它意味着GenerateConditions沒有返回你需要的數據結構,你應該改變它以提供正確的數據,不再有子查詢
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.