Linq:使用三个嵌套级别进行查询

所以我有三张桌子:

CREATE TABLE tblUser ( [pkUserID] [int] IDENTITY(1,1) NOT NULL, [userName] [varchar](150) NULL, [fkCompanyID] [int] NOT NULL ) CREATE TABLE tblCompany ( [pkCompanyID] [int] IDENTITY(1,1) NOT NULL, [name] [varchar](255) NULL ) CREATE TABLE tblSystem ( [pkSystemID] [int] IDENTITY(1,1) NOT NULL, [systemName] [varchar](150) NULL, [fkCompanyID] [int] NULL ) 

这些是我的数据传输对象:

 public class SystemDTO { public int pkSystemId { get; set; } public string Name { get; set; } public int? fkCompanyId { get; set; } } public class CompanyDTO { public int pkCompanyId { get; set; } public string Name { get; set; } public IEnumerable Systems { get; set; } } public class UserDTO { public int pkUserId { get; set; } public string Name { get; set; } public IEnumerable Companies { get; set; } } 

这是我想要做的Linq查询:

 var result= ( from user in db.tblUsers select new UserDTO() { pkUserId=user.pkUserID, Name=user.realName, Companies= ( from company in db.tblCompanies where user.fkCompanyID==company.pkCompanyID select new CompanyDTO() { pkCompanyId=company.pkCompanyID, Name=company.name, Systems= ( from system in db.tblSystem where system.fkCompanyId==company.pkCompanyId select new SystemDTO() { pkSystemId=system.pkSystemID, Name=system.systemName, fkCompanyId=system.fkCompanyID } ) } ) } ).ToList(); 

此查询的问题在于最内部的查询

 from system in db.tblSystem where system.fkCompanyId==company.pkCompanyId select new SystemDTO() { pkSystemId=system.pkSystemID, Name=system.systemName, fkCompanyId=system.fkCompanyID } 

导致linq将sql转换为每个实体一个选择。 我知道我可以跳过select并循环结果并设置属性。 像这样:

 var lsSystem= db.tblSystem.Select (s =>new SystemDTO(){pkSystemId=s.pkSystemID,Name=s.systemName,fkCompanyId=s.fkCompanyID}).ToList(); foreach (var user in result) { foreach (var company in user.Companies) { company.Systems=lsSystem.Where (a =>a.fkCompanyId==company.pkCompanyId).ToList(); } } 

这将导致linq执行两个选择而不是每个实体一个。 所以现在回答我的问题。 有没有其他方法这样做? 我可以用另一种方式填充内部集合吗?

任何建议将不胜感激

编辑
建议是使用loadoption。 我找不到系统和公司之间的负载选项。 但我可以包括之间的loadoption。 公司和用户是这样的:

 var option=new DataLoadOptions(); option.LoadWith(a=>a.fkCompanytblUsers); db.LoadOptions=option; 

但这对查询没有影响,它仍然被翻译成许多选择

EDIT2

正如在答案评论中所述,加载选项不适用于此类linq查询。

好的,这是一个提议,您可以使用它来获取单个查询中的所有内容。 我将简化数据模型以用于演示目的:

 select * from ParentTable join ChildLevel1 on ... join ChildLevel2 on ... 

该查询将同时为您提供所有三个树级别。 这将是非常有效的。 但数据将是多余的。 您需要进行一些客户端处理以使其再次可用:

 var parents = from x in queryResults group x by new { /* all parent columns here */ }) into g select new Parent() { ParentData = g.Key, Children1 = from x in g group x by new { /* all ChildLevel1 columns here */ }) into g select new Child1() { Child1Data = g.Key, Children2 = ... //repeat } } 

您需要通过分组来删除冗余。 换句话说:查询已对数据进行非规范化,我们需要再次对其进行规范化。

这种方法非常麻烦但速度很快。

我自己搞清楚了。 我能看到的最好的方法就是这样做(但如果其他人有更好的建议请再加上):

 var lsSystem= db.tblSystem.Select (s =>new SystemDTO() { pkSystemId=s.pkSystemID, Name=s.systemName, fkCompanyId=s.fkCompanyID } ).ToLookup (s =>s.fkCompanyId); 

然后在linq查询中使用lsSystem,如下所示:

 var result= ( from user in db.tblUsers select new UserDTO() { pkUserId=user.pkUserID, Name=user.realName, Companies= ( from company in db.tblCompanies where user.fkCompanyID==company.pkCompanyID select new CompanyDTO() { pkCompanyId=company.pkCompanyID, Name=company.name, Systems=lsSystem[company.pkCompanyID] } ) } ).ToList(); 

这将导致两个选择语句一个用于系统,一个用于公司用户

您是否查看了LoadOptions和更具体的LoadWith

这将阻止Linq2sql延迟加载并将进行急切加载。

这里有一个简单的例子: http : //davidhayden.com/blog/dave/archive/2007/08/05/LINQToSQLLazyLoadingPropertiesSpecifyingPreFetchWhenNeededPerformance.aspx