2012-01-28 75 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