2012-02-17 18 views
9

EF kullanarak veritabanından kayıt almak ve değerleri bir DTO sınıfına atamak istiyorum. Bir Linq sorgusu için aşağıdaki tablolara göz atın.Linq Sorgu sonuçları bir DTO sınıfına dönüştürebilir

TableA, TableB, her TableA kayıt için TableC

TABLEB içinde birden fazla kayıt var. Her bir TableB kaydı için TableC'de birden fazla kayıt vardır. Şimdi

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

: Şimdi benim DTOs bu

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 
} 

benim linq sorgusu gibi bakmak şöyle sorgu sonucundaki öğeler arasında benim haritalama sınıf I döngü bu

var qry = from ent in TableA 
      select ent; 

gibi görünür Bu, TableA'daki tüm alanlar için çalışır ve veritabanından bir kayıt çıkarır ve TableA_DTO'da TableA tablosundaki her alan için gereken özellikleri ayarlar. Ben Bu yapılabilir da adı TableC_records

tarafından TableB_DTO en özelliğinde TableC uygun tüm kayıtları isim TableB_records tarafından ve ayrıca TableB_DTO içinde TableA mülkiyet alanındaki TABLEB tüm eşleşen kayıtları doldurmak istiyor? Neyi değiştirmem gerekiyor? Yani ben bir fabrika yöntemini yapacak

+3

Eğer Varlık Framework Poco en (bazen kazaen Kod İlk olarak adlandırılan aka DBContext,) kullanamazsınız bir neden var mı? Temel olarak, DTO'lara olan ihtiyacı ortadan kaldırabilir ve bunun yerine EF POCO'ları kullanabilir misiniz? – JMarsch

+1

AutoMapper'ı kullanmayı düşündünüz mü? DTO'larınızın ne kadar farklı olduğuna bağlı olarak, bu, eşlemeyi yapmak için iki veya üç kod satırı kadar basit olabilir. – Robaticus

+0

@jMarsch: Veritabanı zaten orada edmx yolu gitti – user20358

cevap

6

DTO'nuzu List'dan IEnumerable'a ve LINQ sorgusundaki herşeyi değiştiririm.

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

Ancak bununla ilgili sorun, bunun 'N + 1' sorgularından çok daha fazlasını tetiklemesidir; M * (N + 1) + 1 'sorgularını tetikler, neredeyse kesinlikle çok zayıf performansa neden olur. – Steven

+3

@Steven - Yanlış. Sadece bir sorgu gönderir. – Aducci

+0

Evet, "TableB_records" ve "TableV_records" özellikleri üzerinde yinelemeye başlayana kadar öyle. SQL profiler kullanılarak yürütülen tek bir sorguya yakından bakın. 'TableB' ve' TableC' hakkında tüm bilgilerin eksik olduğunu fark edeceksiniz. – Steven

0

linq sorgusu yoksa benim haritalayan zaman için

Teşekkür yapmak yolu ... Is: TableA_DTO CreateDTO(TableAItem item);

bu kullanarak, sadece sorgu yeniden başladı gibi:

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

Bu, doğrudan "DTO" nesnelerinin toplanmasını sağlar.

Bu durum, Varlık Çerçevesi kullanıyorsanız, en son sürümlerde eklenen EF Code First bu durumda daha kullanışlı olabilir.

+0

'dur. CreateDTO nedir? bir sınıf mı? onun tanımı ne olurdu? – user20358

+0

@ user20358 Yazdığınız bir yöntem olabilir, bu ödevleri yapar. Hala bu atamaları yapmak gerekir, ama bir yöntemi (yani varlıktan dönüştürür -> DTO) ile sınırlı olurdu –

+0

@Reed: çağrılması 'AsEnumerable()' a IQueryable 'üzerinde aşağı çekin sağlayacaktır veritabanından tüm satırlar. Tablonun bin satırdan (ve tüm verilerinden) daha az olması veya tüm kayıtları almak istemediğiniz sürece, bu performans için çok kötü olacaktır. – Steven

4

İlk olarak, Entity Framework 4.1 ve POCO'ları (DbContext) kullanıp kullanamayacağınızı ve DTO'nun altoghther ihtiyacını ortadan kaldırabileceğinizi sormam gerekiyor.

Yanıtın hayır olduğunu varsayalım, bunun nedeni tüm alanları geri çekmemeniz veya bir şekilde verilerin "şeklini" değiştirmektir.

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

bu şekilde yapmanın faydası: Bu durumda

, böyle bir şey bakmak için LINQ sorgusu değiştirebilecek SQL Profiler açarsanız, bunu sadece sütunları görmek gerektiğini Bunu gerçek SQL sorgusuna yapmasını talep edersiniz. Önce hepsini sorgular ve sonra değerleri çekerseniz, tüm sütunlar kablodan aşağı çekilir.

0

GÜNCELLEME

diğerleri belirttiği gibi, (aşağıda gösterildiği gibi) Varlık Framework 4.0 ile çalışırken onun için verimli bir düzleştirilmiş sonuca LINQ sorgusu çevirebilir beri gerekli değildir sonuçlarını düzleştirme sen. Bu nedenle, aşağıdaki kod yalnızca LINQ ile SQL (veya diğer LINQ sağlayıcıları) ile çalışırken gereklidir. Bu davranışı yalnızca Oracle üzerinden değil SQL üzerinden test ettiğimi unutmayın, çünkü bu davranış LINQ sağlayıcısına özel olabilir, yani Oracle sağlayıcısı (hala beta sürümünde) veya Oracle için ticari Devart sağlayıcısı hala N + yapmanız çalıştığımız şey 1.


bir ağaç gibi yapılandırılmıştır nesneleri kümesi elde etmektir. Herhangi bir özel dikkat olmadan, veritabanına birçok sorguyu tetikleyeceksiniz. Yuvalamanın bir düzeyi ile N + 1 sorgularını tetiklersiniz, ancak yuvalarınız iki seviye derin olduğundan, M x (N + 1) + 1 sorgularını tetikleyeceksiniz. performans (veri kümenizin boyutu ne olursa olsun). İstediğiniz, veritabanına gönderilen tek bir sorgu olduğundan emin olmaktır. Bunu sağlamak için, verileri iyi düzleştiren bir ara sorgu oluşturmalısınız, aynen eski SQL günlerinde yaptığınız gibi, ağaç gibi veriyi almak için :-). aşağıdaki örneğe bakınız:

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

sonraki adım bu bellek veri yapısı dönüştürmektir (kullanarak anonim tür o) DTO ağaç yapısı nesnelerini:

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

zamanda Gördüğünüz gibi, çözümün ilk kısmı aslında bunu yapmak için 'eski' yoldur, SQL sorgularımızı el ile yazdığımızda geri dönüyoruz. Ancak, bu yazılan bellek içi veri kümesini aldıktan sonra, bu verileri istediğimiz yapıda almak için tekrar LINQ (Objects) 'a dönüştürebiliyoruz.

Not: Bu ayrıca, sayfalama ve sıralama yapmanızı da sağlar. Bu biraz daha zor olacak, ama kesinlikle imkansız değil.

+1

Entity Framework bunu sizin için yaptığından, tüm "düzleştirme" adımı tamamen gereksizdir. @ Aducci'nin stratejisini kullanmak, tek bir veritabanı sorgusunun sonuçları SQL'den gelen düzleştirilmiş satırlarda döndürmesine neden olacak ve bu değerleri otomatik olarak hiyerarşik bir yapıya dönüştürecektir. – StriplingWarrior

+0

@StriplingWarrior: Bazı testler yaptıktan sonra, bu konuda kesinlikle haklısınız. Entity Framework bana burada göz kamaştırdı :-) Sonunda LINQ to SQL, N + 1 sorguları yaptığından, sonunda LINQ'dan SQL'e bir şey çıkarır. Bu gerçekten harika. – Steven

+0

Evet, LINQ to SQL'in yuvalanmış deseni bir seviye derinliğe (TableA ve TableB) taşıdığı anlaşılıyor, ancak bundan daha derine iniyorsunuz ve TableC'deki her öğe için ayrı bir gidiş-dönüş ile sonuçlanıyorsunuz. – StriplingWarrior

İlgili konular