2012-01-28 46 views
4
查询

所以我有三个表: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<SystemDTO> Systems { get; set; } 
} 

public class UserDTO 
{ 
    public int pkUserId { get; set; } 
    public string Name { get; set; } 
    public IEnumerable<CompanyDTO> 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转换为每个实体选择一个。我知道我可以跳过选择并循环结果并设置属性。像这样:

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做两个选择,而不是每个实体。所以,现在我的问题。有没有其他的方式来做到这一点?我可以用另一种方式填充内部集合吗?

任何暗示将理解

EDIT
甲暗示是使用loadoption。系统和公司之间找不到负载。但是我可以在两者之间加入加载选项。公司与用户是这样的:

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

但这对查询没有影响它仍然是翻译成很多选择

EDIT2

正如在回答说评论负载选项不申请这个linq查询。

+0

谁投下了这个问题。请解释.. – Arion 2012-02-23 06:58:06

回答

3

好吧,这里是你的使用来获得在单个查询一切的建议。我将简化数据模型进行演示:

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 
      } 
} 

您需要做分组去除冗余。换句话说:查询已经对数据进行了非规范化处理,我们需要再次进行归一化处理。

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

+0

如果我在每个表中有300-1000行,那么在客户端执行group by s有效吗?或者是其他建议更有效率? – Arion 2012-01-28 17:57:43

+1

分组发生在客户端,但您的服务器端代码将达到最高效率。这样做会更有效率,然后做大量的往返。客户对所有数千个对象进行分组没有任何问题。那真的没什么。 – usr 2012-01-28 18:12:25

+1

如果您想要获得最佳性能,请使用本产品。 – usr 2012-01-28 18:13:06

0

你看过LoadOptions和更具体的LoadWith

这将阻止Linq2sql从延迟加载,并会做急切的加载。

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

+0

是的,这也是一种做法。但是我目前工作的数据库已经很老了,并且缺少目标表之间的关系。有些负载选项,我发现我更新了这个问题。 – Arion 2012-01-28 11:53:20

+0

您可以先选择一个扁平结果集,然后将其移入内存中,例如List <>,并将该列表用作创建DTO的输入。这应该给你一个选择与许多外部连接。 – Pleun 2012-01-28 12:11:08

+0

你能为此添加一个例子吗? – Arion 2012-01-28 12:17:51

1

我找出自己。我所看到的最好的办法是做这样的(但如果重新别人有更好的建议,请添加它们):

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

然后使用lsSystem在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=lsSystem[company.pkCompanyID] 
        } 
       ) 
     } 
    ).ToList(); 

这将导致两个SELECT语句一个系统,一个用于用户企业

+1

这是正确的解决方案,即使它有点不方便。 SQL在返回树时有问题。 – usr 2012-01-28 16:17:36

+0

是的,我认为如此。但我想我会留到明天。也许有人来过,找到我们没有想过的东西 – Arion 2012-01-28 17:12:15