2012-01-28 12 views
4

だから私は3つのテーブルれている:これらは私のデータ転送オブジェクトのLINQ:ネストされた3つのレベルのクエリ

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をエンティティごとに1つの選択に変換します。私は選択をスキップして結果をループし、プロパティを設定できることを知っています。このように:

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はエンティティごとに1つではなく2つの選択を行うようになります。だから今私の質問に。これを行う別の方法がありますか?インナーコレクションを別の方法で取り込むことはできますか?任意示唆

EDIT
示唆はloadoptionを使用することで理解されるであろう。私はシステムと会社の間に負荷を見つけることができません。しかし、私は間にloadoptionを含めることができます。当社及び利用者、このような:

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

しかし、これは、それはまだのようにコメントロード・オプションがない答えで述べて多くの選択

EDIT2

に翻訳されたクエリには影響しませんこのようなlinqクエリを適用します。

+0

誰がこの質問に投票しました。説明してください。 – Arion

答えて

3

オクラホマへのユーザーのためのシステムのための1と1になります、ここにあなたが使用し、単一のクエリ内のすべてを取得する提案です。デモ目的でデータモデルを簡略化します。

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

このクエリは3つのツリーレベルすべてを一度に提供します。それは非常に効率的です。しかし、データは冗長になります。

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行を持っていれば、クライアント側でグループを行うのは効果的ですか?あるいは、他の提案はより効率的ですか? – Arion

+1

グループ化はクライアント側で行われますが、サーバー側のコードは最大限に効率的です。大量の往復を行う方がずっと効率的です。クライアントは何千ものオブジェクトをすべてグループ化しても問題はありません。それは本当に何もない。 – usr

+1

もしあなたが最高のパフォーマンスを望むなら、これを使ってください。 – usr

0

LoadOptions、特にLoadWithを調べましたか?

これにより、Linq2sqlの遅延読み込みが停止され、熱心な読み込みが行われます。

ここに簡単な例: http://davidhayden.com/blog/dave/archive/2007/08/05/LINQToSQLLazyLoadingPropertiesSpecifyingPreFetchWhenNeededPerformance.aspx

+0

はい、それもやっている方法です。しかし、私が現在取り組んでいるデータベースはかなり古く、ターゲットテーブル間の関係が欠落しています。いくつかのloadoption私と私はそれで質問を更新見つける。 – Arion

+0

フラットな結果セットを最初に選択し、そのリストをメモリに移動してList <>を調べ、そのリストを入力として使用してDTOを作成することができます。それはあなたに多くの外部結合を選択する必要があります。 – Pleun

+0

これに例を追加できますか? – Arion

1

私は自分自身をそれを把握。私が見ることができるもの最良の方法は、(誰がより良い提案を持って再び場合は、それらを追加してください)このようにそれを行うことです。その後、

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(); 

これは、2つのSELECT文の企業

+1

少し不便であっても、これは正しい解決策です。 SQLにはツリーを返す際に問題があります。 – usr

+0

そうですね。しかし、私は明日までそれを残すと思う。誰かが来て、私たちが考えなかったものを見つけるかもしれない – Arion

関連する問題