2014-10-22 44 views
0

問題在最後。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 
+0

你比較了產生查詢(通過L2S和EF)? – Maarten 2014-10-22 09:11:55

+0

@Maarten老實說,我只是檢查一下EF是否只打了一次數據庫。但是L2S版本使用了與該視圖等價的功能,並且SSMS中的EF sql查詢執行時間不到0秒。我通過去除串聯(將Codes2AsString設置爲「」)來測試,並且網格填充速度更快。 – tschmit007 2014-10-22 09:20:13

+0

你確定Codes2AsString計算不會發出子選擇又名SELECT N + 1嗎? – Firo 2014-10-22 09:39:42

回答

0

你已經過於複雜的事情上火這個位。 下面的查詢應該工作,這將是一個數據庫連接,無子查詢:

var givenValue = "t1"; 
var heads = new[] { // your repo.Heads query here: heads = repo.Heads.Where(w.Expand()).OrderBy(x => x.IdFolder).Take(taked); 
    new {IdFolder = 1, Details = new[]{new {Code = "a", IType = "t1"}, new {Code = "b", IType = "t2"}}}, 
    new {IdFolder = 2, Details = new[]{new {Code = "c", IType = "t2"}, new {Code = "d", IType = "t1"}}}, 
}; 

// Db hit. 
var q = heads; 
var details = q.SelectMany(
    h=>h.Details 
     .Where(d=>d.IType == givenValue) 
     .Select(d=>new{HeadId = h.IdFolder, d.Code})).ToList(); 

// O(N) in-memory. 
var grid = details 
    .ToLookup(d=>d.HeadId) 
    .Select(g=>new{HeadId = g.Key, Codes = string.Join(",",g.Select(i=>i.Code))}) 
    .ToList(); 
+0

查詢不會生成sql子查詢。 linq子查詢只允許限制返回的列。 – tschmit007 2014-10-22 19:26:37