2014-10-22 42 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