问题在最后。
以下是案例
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;}
}
我需要填充一个至少有两列的网格:
我的第一次尝试是:
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 to 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;}
}
我将这个新类映射到视图并修改我的Head类
public class Head {
public Int32 Id {get; set;}
public virtual ICollection<Detail> Details {get; set;}
public virtual VHead2Codes Codes2AsString {get; set;}
}
我设置了One To One关系,我的查询变为
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实现过程使用了丢失的微处理器周期。 但这可能是错误的(见第二条评论)。串联中的循环丢失:在头部和细节上循环。
我的问题是:还有另一种方法可以通过保持性能来避免视图吗?
=============================================== ======================
=======根据您的要求,生成的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 :(得分:0)
你的东西过于复杂了。 以下查询应该有效,它将是db中的连接,没有子选择:
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();