2012-02-17 11 views
9

EFを使用してデータベースからレコードを取得し、その値をDTOクラスに割り当てたいとします.Linqクエリでは次の表を参考にしてください。Linqクエリ結果をDTOクラスにマッピング

TableAの、TableBの、各テーブルAのレコードの表C

は、TableBの中に複数のレコードがあります。各TableBレコードには、TableCに複数のレコードがあります。今

foreach (var dataitem in query) 
    { 
     TableA_DTO dto = new TableA_DTO(); 
     dto.tableA_rowid = dataitem.ID; 
     //remaining field definitions here 
    } 

: 今私のDTOはこの

public class TableA_DTO 
{ 
    public int tableA_rowid { get; set; } 
    //remaining tableA field definitions 

    public List<TableB_DTO> TableB_records { get; set; } 
} 

public class TableB_DTO 
{ 
    public int tableB_rowid { get; set; } 
    //remaining tableB field definitions 

    public List<TableC_DTO> TableC_records { get; set; } 
} 

public class TableC_DTO 
{ 
    public int tableC_rowid { get; set; } 
    //remaining tableC field definitions 
} 

私のLINQクエリのように見えるがそうのようなクエリ結果内の項目を私のマッピングクラスIのループでは、この

var qry = from ent in TableA 
      select ent; 

ようになりますこれは、データベースから1つのレコードを取り出し、テーブルTableAの各フィールドのTableA_DTOに必要なプロパティを設定する、TableAのすべてのフィールドで機能します。私はまた、TableBプロパティのフィールドにTableBプロパティのフィールドにTableB_recordsという名前のすべての一致するレコードを、TableB_recordsという名前で、またTableB_DTOにTableC_recordsという名前でTableB_DTOのプロパティでTableCからのすべての一致するレコードを取り込みたいとします。

これはできますか?私は何を変更する必要がありますか?それは私がファクトリメソッド、すなわちになるだろう

+3

Entity Framework POCO(別名DbContext、時に誤ってコードファーストと呼ばれる)を使用できない理由はありますか?基本的には、DTOの必要性を排除し、代わりにEF POCOを使用できますか? – JMarsch

+1

AutoMapperの使用を検討しましたか?あなたのDTOがどのように違うかによって、これはマッピングを行うための2行または3行のコードと同じくらい簡単です。 – Robaticus

+0

@jMarsch:データベースはすでにそこにありますので、edmxの方法で行っています – user20358

答えて

6

私はあなたのDTOをListからIEnumerableに変更し、LINQクエリのすべてを実行します。

var query = 
    from ent in TableA 
    select new TableA_DTO 
    { 
     TableAProperty = a.Property, 
     TableB_records = 
      from b in TableB 
      where ent.Key == b.Key 
      select new TableB_DTO 
      { 
       TableBProperty = b.Property, 
       TableC_records = 
        from c in TableC 
        where b.Key == c.Key 
        select new TableC_DTO 
        { 
         TableCProperty = c.Property 
        } 
      } 
    }; 
+0

しかしこの問題は、これが 'N + 1'クエリよりもはるかに多くをトリガするということです。それは 'M *(N + 1)+ 1'クエリをトリガして、パフォーマンスが非常に悪くなることはほとんどありません。 – Steven

+3

@スティーブン - 間違っています。 1つのクエリだけを送信します。 – Aducci

+0

はい、 'TableB_records'と' TableV_records'プロパティを反復処理するまでです。 SQLプロファイラを使用して実行されるその単一のクエリを注意深く見てください。 'TableB'と' TableC'に関するすべての情報が欠落していることに気づくでしょう。 – Steven

0

... LINQクエリまたは私のやり方は、私のマッピングお時間を

感謝です:TableA_DTO CreateDTO(TableAItem item);

はこれを使用して、あなたは自分のクエリを書き直すことができ次のようになります。

IEnumerable<TableA_DTO> = TableA.AsEnumerable().Select(CreateDTO); 

これは、「DTO」オブジェクトのコレクションを直接提供します。

つまり、エンティティフレームワークを使用している場合は、最近のバージョンで追加されたEF Code Firstがこの場合にはさらに役立つ可能性があります。

+0

のCreateDTOとなっています。それはクラスですか?その定義は何でしょうか? – user20358

+0

@ user20358あなたが書いている方法ですが、それは割り当てを行います。 @Reed: 'IQueryable 'で 'AsEnumerable()'を呼び出すことで、確実にプルダウンすることができます(これは、エンティティから変換する1つのメソッドに限定されます)。 –

+0

@Reed:データベースからのすべての行テーブルに1000行(およびすべてのデータ)未満のものがある場合を除き、またはすべてのレコードを取得しない限り、これはパフォーマンス上非常に悪いことになります。 – Steven

4

まず、Entity Framework 4.1とPOCO(DbContext)を使用できるかどうかを尋ね、DTOの必要性を回避するかどうか尋ねるだけです。

回答が「いいえ」であると仮定すると、フィールドのすべてを取り戻さない、またはデータの「形状」を何らかの形で変更しているためである必要があります。

from t in table 
where ... 
select new DTOA() 
{ 
    TheDtoProperty = theTableProperty, 
    AndSoOn = AndSoOn 
}; 

このようにそれをすることの利点:その場合は

、あなたはこのような何かを見て、あなたのLINQクエリを変更することができますが、SQLプロファイラをオンにする場合は、そのカラムだけが、その表示されるはずですあなたはそれを実際のSQLクエリにするように要求します。最初にすべてを照会して値をプルすると、すべての列がワイヤーにプルダウンされます。

0

UPDATE

他の人が指摘したように、(以下に示すように)エンティティフレームワーク4.0で作業する場合、それはのための効率的な平坦化の結果にLINQクエリを変換することができるので、不要な結果を平坦化君は。したがって、次のコードは、LINQ to SQL(または他のLINQプロバイダ)で作業する場合にのみ必要です。この動作はLINQプロバイダ固有のものである可能性があるため、これはOracleプロバイダ(まだベータ版)またはOracleの商用Devartプロバイダが引き続きN +何をしようとする1


木のように構成されているオブジェクトのセットを取得することです。特別な注意を払うことなく、データベースへの多くのクエリをトリガします。 1つのレベルのネストでは N + 1のクエリがトリガーされますが、ネストが2レベル深いので、 M x(N + 1)+ 1のクエリがトリガーされます。パフォーマンス(データセットのサイズに関係なく)必要なのは、データベースに送信されるクエリが1つだけであることを確認することです。これを確実にするには、古き良きSQL時代に行ったように、結果を平坦化する中間クエリを作成し、データのようなツリーを取得する必要があります。次の例を見てみましょう

var records = 
    from record in db.TableC 
    where ... // any filtering can be done here 
    select record; 

// important to call ToArray. This ensures that the flatterned result 
// is pulled in one single SQL query. 
var results = (
    from c in records 
    select new 
    { 
     tableA_rowid = c.B.A.Id, 
     tableA_Prop1 = c.B.A.Property1, 
     tableA_Prop2 = c.B.A.Property2, 
     tableA_PropN = c.B.A.PropertyN, 
     tableB_rowid = c.B.Id, 
     tableB_Property1 = c.B.Property1, 
     tableB_Property2 = c.B.Property2, 
     tableB_PropertyN = c.B.PropertyN, 
     tableC_rowid = c.Id, 
     tableC_Property1 = c.Property1, 
     tableC_Property2 = c.Property2, 
     tableC_PropertyN = c.PropertyN, 
    }) 
    .ToArray(); 

は、次のステップは、DTOオブジェクトのツリー構造に(その匿名型を使用して)、そのメモリ内のデータ構造を変換することである。

// translate the results to DTO tree structure 
TableA_DTO[] dtos = (
    from aresult in results 
    group aresult by aresult.tableA_rowid into group_a 
    let a = group_a.First() 
    select new TableA_DTO 
    { 
     tableA_rowid = a.tableA_rowid, 
     tableA_Prop1 = a.tableA_Prop1, 
     tableA_Prop2 = a.tableA_Prop2, 
     TableB_records = (
      from bresult in group_a 
      group bresult by bresult.tableB_rowid into group_b 
      let b = group_b.First() 
      select new TableB_DTO 
      { 
       tableB_rowid = b.tableB_rowid, 
       tableB_Prop1 = b.tableB_Prop1, 
       tableB_Prop2 = b.tableB_Prop2, 
       TableC_records = (
        from c in group_b 
        select new TableC_DTO 
        { 
         tableC_rowid = c.tableC_rowid, 
         tableC_Prop1 = c.tableC_Prop1, 
         tableC_Prop2 = c.tableC_Prop2, 
        }).ToList(), 
      }).ToList() 
    }) 
    .ToArray(); 

としてあなたが見ることができます、ソリューションの最初の部分は、実際にはこれを行うの '古い'方法です、私たちは手でSQLクエリを書くときに戻る。ただし、型指定されたメモリ内データが取得されたら、LINQ(Objectsへ)を再度利用して、このデータを必要な構造で取得することができます。

これにより、ページングとソートを行うこともできます。これはちょっと難しいかもしれませんが、確かに不可能ではありません。

+1

Entity Frameworkがこれを行うので、全体的な「平坦化」ステップはまったく必要ありません。 @ Aducciの戦略を使用すると、1つのデータベースクエリでSQLから平坦化された行の結果が返され、自動的にこれらの値が階層構造に合成されます。 – StriplingWarrior

+0

@StriplingWarrior:いくつかのテストをした後、あなたはそれについて絶対に正しいと思われます。 Entity Frameworkは私をここでびっくりさせました:-)最後に、LINQ to SQLはN + 1クエリを実行するので、LINQ to SQLより優れています。これは確かにクールです。 – Steven

+0

ええ、それはLINQ to SQLがネストされたパターンを1レベル深く(TableAとTableB)扱うようですが、それより深いところではTableCの各項目について別々のラウンドトリップが行われます。 – StriplingWarrior

関連する問題