問題在最後。IEnumerable屬性聚合/連接的具體化和性能
這裏是
public class Head {
public Int32 Id {get; set;}
public virtual ICollection<Detail> Details {get; set;}
}
public class Detail {
public Int32 Id {get; set;}
public virtual Head Head {get; set;}
public Int32 IType {get; set;}
public String Code {get; set;}
}
我需要的是填補了網格至少兩列的情況:
- Head.Id
- 級聯碼值用於表示頭部和一給出Detail.Type值。
我第一次嘗試是:
Int32 givenValue = 2;
var q = repo.Heads.
Where(w.Expand()).
Select(x => new {
Id = x.IdFolder,
Details = x.Details.Select(y => new {
Id = y.Id,
IType = y.IType,
Code = y.Name
})
}).OrderBy(x => x.Id).Take(taked).
ToList(). // One hit to the database
Select(y => new {
Id = y.Id,
Codes2AsString = String.Join(
",",
y.Details.Where(z => z.IType == givenValue).Select(z => z.Code))
}).
ToList();
它工作正常。 (我知道我應該過濾數據庫端的細節,但我需要整套其他連接。)
但是:這段代碼慢了8到10,相當於Linq到SQL(我正在遷移現有的應用程序)爲2850頭。這需要4到5秒才能填滿網格,而不是接近0秒。
我的第二次嘗試是在數據庫端進行聚合/連接,就像在舊版本的應用程序中一樣。
我創建一個視圖(用TSQL特異性)
create view as v_Head2Codes
select
h.Id,
(
select ',' + id.Code as [text()]
from
Details id
Where
id.Header_Id == h.Id and id.IType = 2
order by id.Code
For XML PATH ('')
) Codes
from
Headers h
然後創建一個新的類
public class VHead2Codes {
public Int32 Id {get; set;}
public String Codes {get; set;}
}
我這個新類映射到視圖和修改我的頭班
public class Head {
public Int32 Id {get; set;}
public virtual ICollection<Detail> Details {get; set;}
public virtual VHead2Codes Codes2AsString {get; set;}
}
我設置了一對一的關係,我的查詢變成了
var q = repo.Heads.
Where(w.Expand()).
Select(x => new {
Id = x.IdFolder,
Codes2AsString = x.Codes2AsString.Codes
}).OrderBy(x => x.Id).Take(taked).
ToList(); // One hit to the database
這裏我得到了和以前一樣的結果和相同的性能。
我的第一個猜測是EF實現過程使用了丟失的微處理器週期。 但它可能是錯誤的(請參閱第二條評論)。循環在串聯:循環遍歷頭部和細節上丟失。
我的問題是:有沒有另一種方式允許通過保持perfs來避免視圖?
============================================== =======================
=======對您的請求,生成的SQL ========= =================
LINQ查詢是:
Folders.Select(x => new {
Id = x.IdFolder,
Contribs = x.Contributors.Select(y => new {
Name = y.Contributor.LastName
})
})
的SQL:
SELECT
[Project1].[idDossier] AS [idDossier],
[Project1].[C1] AS [C1],
[Project1].[ThirdParty_Id] AS [ThirdParty_Id],
[Project1].[LastName] AS [LastName]
FROM (SELECT
[Extent1].[idDossier] AS [idDossier],
[Join1].[ThirdParty_Id] AS [ThirdParty_Id],
[Join1].[LastName] AS [LastName],
CASE WHEN ([Join1].[ThirdParty_Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[tableD] AS [Extent1]
LEFT OUTER JOIN (SELECT [Extent2].[ThirdParty_Id] AS [ThirdParty_Id], [Extent2].[TableD_Id] AS [TableD_Id], [Extent3].[LastName] AS [LastName]
FROM [dbo].[FolderContributions] AS [Extent2]
INNER JOIN [dbo].[v_ThirdParties] AS [Extent3] ON ([Extent2].[ThirdParty_Id] = [Extent3].[Id]) AND ([Extent2].[ThirdParty_Source] = [Extent3].[Source])) AS [Join1] ON [Extent1].[idDossier] = [Join1].[TableD_Id]
) AS [Project1]
ORDER BY [Project1].[idDossier] ASC, [Project1].[C1] ASC
你比較了產生查詢(通過L2S和EF)? – Maarten 2014-10-22 09:11:55
@Maarten老實說,我只是檢查一下EF是否只打了一次數據庫。但是L2S版本使用了與該視圖等價的功能,並且SSMS中的EF sql查詢執行時間不到0秒。我通過去除串聯(將Codes2AsString設置爲「」)來測試,並且網格填充速度更快。 – tschmit007 2014-10-22 09:20:13
你確定Codes2AsString計算不會發出子選擇又名SELECT N + 1嗎? – Firo 2014-10-22 09:39:42