2012-02-17 13 views
9

पर मैं ईएफ का उपयोग कर डेटाबेस से रिकॉर्ड प्राप्त करना चाहता हूं और मानों को एक डीटीओ कक्षा में असाइन करना चाहता हूं। लिंक क्वेरी के लिए निम्न तालिकाओं पर विचार करें।मैकिंग लिंक क्वेरी का परिणाम डीटीओ कक्षा

TableA, TableB, TableC

प्रत्येक TableA रिकॉर्ड के लिए वहाँ TableB में कई रिकॉर्ड कर रहे हैं। प्रत्येक टेबलबी रिकॉर्ड के लिए टेबलसी में कई रिकॉर्ड हैं। अब मेरी DTOs इस

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 क्वेरी की तरह लग रहे तो जैसे क्वेरी परिणाम में आइटम के माध्यम से इस

var qry = from ent in TableA 
      select ent; 

मेरी मानचित्रण वर्ग मैं पाश में तरह दिखता है:

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

अब यह TableA के सभी क्षेत्रों के लिए काम करता है जहां यह डेटाबेस से एक रिकॉर्ड लाता है और तालिका TableA में प्रत्येक फ़ील्ड के लिए TableA_DTO में आवश्यक गुण सेट करता है। मैं TableB_records नाम से tableA प्रॉपर्टी फ़ील्ड में टेबलबी में सभी मेलिंग रिकॉर्ड्स को भी पॉप्युलेट करना चाहता हूं और TableB_DTO में TableB_DTO की संपत्ति में टेबलसी से सभी मेल खाने वाले रिकॉर्ड्स को तालिकाC_records

क्या किया जा सकता है? मुझे बदलने की क्या ज़रूरत है? अर्थात यह LINQ क्वेरी या जिस तरह से मैं अपने मानचित्रण

अपने समय के लिए धन्यवाद करना ...

+3

क्या कोई कारण है कि आप एंटिटी फ्रेमवर्क पीओसीओ (उर्फ डीबीकॉन्टेक्स्ट, कभी-कभी गलती से पहले कोड कहा जाता है) का उपयोग नहीं कर सकते हैं? असल में, क्या आप डीटीओ की आवश्यकता को खत्म कर सकते हैं और इसके बजाय ईएफ पॉको का उपयोग कर सकते हैं? – JMarsch

+1

क्या आपने ऑटोमैपर का उपयोग करने पर विचार किया है? आपके डीटीओ कितने अलग हैं, इस पर निर्भर करते हुए, मैपिंग करने के लिए कोड की दो या तीन पंक्तियों जितनी सरल हो सकती है। – Robaticus

+0

@jMarsch: डेटाबेस पहले से मौजूद है इसलिए edmx way – user20358

उत्तर

6

मैं आपके डीटीओ को 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' क्वेरी से भी अधिक ट्रिगर करता है; यह 'एम * (एन + 1) + 1' प्रश्नों को ट्रिगर करता है, लगभग निश्चित रूप से बहुत खराब प्रदर्शन का कारण बनता है। – Steven

+3

@ स्टेवन - गलत। यह केवल एक प्रश्न भेजता है। – Aducci

+0

हां यह तब तक करता है जब तक आप 'TableB_records' और' TableV_records' गुणों पर पुनरावृत्ति शुरू नहीं करते। SQL प्रोफाइलर का उपयोग करके निष्पादित की गई एकल क्वेरी पर बारीकी से देखें। आप देखेंगे कि इसमें 'टेबलबी' और' टेबलसी 'के बारे में सारी जानकारी गुम है। – Steven

0

मैं एक कारखाने विधि बनाना होगा है: TableA_DTO CreateDTO(TableAItem item);

इस का उपयोग करना, तुम सिर्फ आपकी क्वेरी पुनर्लेखन सकता है के रूप में:

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

इससे आपको सीधे "डीटीओ" ऑब्जेक्ट्स का संग्रह मिल जाएगा।

कहा जा रहा है कि, यदि आप एंटीटी फ्रेमवर्क का उपयोग कर रहे हैं, तो हाल के संस्करणों में जोड़ा गया EF Code First इस मामले में और अधिक उपयोगी हो सकता है।

+0

है CreateDTO क्या है? क्या यह एक वर्ग है? इसकी परिभाषा क्या होगी? – user20358

+0

@ user20358 यह आपके द्वारा लिखी जाने वाली एक विधि होगी, जो असाइनमेंट करता है। आपको अभी भी उन असाइनमेंट करने की आवश्यकता होगी, लेकिन यह एक विधि (जो इकाई से परिवर्तित हो जाती है -> डीटीओ) –

+0

@Reed: 'IQueryable 'पर' AsEnumerable()' को कॉल करना सुनिश्चित करेगा कि आप नीचे खींचेंगे डेटाबेस से सभी पंक्तियां। जब तक कि तालिका में हजारों पंक्तियों (और उनके सभी डेटा) से कम न हो, या जब तक कि आप सभी रिकॉर्ड प्राप्त नहीं करना चाहते हैं, यह प्रदर्शन के लिए बहुत खराब होगा। – Steven

4

पहली बात, मुझे सिर्फ यह पूछने की ज़रूरत है कि क्या आप एंटीटी फ्रेमवर्क 4.1 और पीओसीओ (डीबीकॉन्टेक्स्ट) का उपयोग कर सकते हैं और डीटीओ के altoghther की आवश्यकता से बच सकते हैं?

मान लीजिए कि उत्तर नहीं है, ऐसा इसलिए होना चाहिए क्योंकि आप सभी क्षेत्रों को वापस नहीं खींच रहे हैं, या आप डेटा के "आकार" को किसी भी तरह बदल रहे हैं।

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

इस तरह से यह कर के लाभ:

उस मामले में, आप कुछ इस तरह देखने के लिए अपनी LINQ क्वेरी को बदल सकता है, तो आप एसक्यूएल प्रोफाइलर चालू करते हैं, आपको लगता है कि केवल कॉलम देखना चाहिए कि आप इसे वास्तविक SQL क्वेरी में बनाने का अनुरोध करते हैं। यदि आप इसे पहले पूछते हैं और फिर मान खींचते हैं, तो सभी कॉलम तार को खींचा जाएगा।

0

अद्यतन

के रूप में अन्य लोगों ने बताया, (जैसा कि नीचे दिखाया गया है) परिणाम सपाट जब इकाई की रूपरेखा 4.0 के साथ काम करने की जरूरत नहीं है, क्योंकि यह के लिए एक कुशल चपटा परिणाम के लिए LINQ क्वेरी का अनुवाद कर सकते हैं आप। इसलिए, निम्न कोड केवल LINQ से SQL (या संभवतः अन्य LINQ प्रदाताओं) के साथ काम करते समय आवश्यक है। ध्यान दें कि मैंने केवल एसक्यूएल सर्वर पर ईएफ के साथ इसका परीक्षण किया है और ओरेकल से अधिक नहीं है, क्योंकि यह व्यवहार LINQ प्रदाता विशिष्ट हो सकता है, जिसका अर्थ है कि ओरेकल प्रदाता (अभी भी बीटा में) या ओरेकल के लिए वाणिज्यिक देवता प्रदाता अभी भी एन + 1.


तुम क्या करने कोशिश कर रहे हैं वस्तुओं है कि एक पेड़ की तरह संरचित कर रहे का एक सेट प्राप्त करने के लिए है। किसी विशेष देखभाल के बिना, आप डेटाबेस में कई प्रश्नों को ट्रिगर करेंगे। घोंसले के एक स्तर के साथ आप एन + 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(); 

अगले कदम है कि में स्मृति डेटा संरचना को बदलने के लिए है (का उपयोग करते हुए कि अज्ञात प्रकार) डीटीओ के वृक्ष संरचना में वस्तुओं:

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

के रूप में आप देख सकते हैं, समाधान का पहला भाग वास्तव में ऐसा करने का 'पुराना' तरीका है, जिस तरह से हम अभी भी हमारे एसक्यूएल प्रश्नों को हाथ से लिखेंगे। हालांकि, अच्छा है कि, जब हम इन-मेमोरी डेटा के इस टाइप किए गए सेट को प्राप्त करते हैं, तो हम इस डेटा को हमारे इच्छित ढांचे में प्राप्त करने के लिए LINQ (ऑब्जेक्ट्स) को फिर से ले सकते हैं।

ध्यान दें कि यह आपको पेजिंग और सॉर्टिंग करने की अनुमति देता है। यह थोड़ा और मुश्किल होगा, लेकिन निश्चित रूप से असंभव नहीं है।

+1

संपूर्ण "फ़्लैटनिंग" चरण पूरी तरह से अनावश्यक है, क्योंकि इकाई फ्रेमवर्क आपके लिए यह करता है। @ एडुची की रणनीति का उपयोग करने से SQL सर्वर से फ़्लैटेड पंक्तियों में परिणाम लौटने के लिए एक डेटाबेस क्वेरी का कारण बन जाएगा, और फिर यह स्वचालित रूप से इन मानों को पदानुक्रमित संरचना में बना देगा। – StriplingWarrior

+0

@StriplingWarrior: कुछ परीक्षण करने के बाद, ऐसा लगता है कि आप इसके बारे में बिल्कुल सही हैं। एंटिटी फ्रेमवर्क ने मुझे यहां चकित किया :-) अंत में यह LINQ से SQL में एक्सेल करता है, क्योंकि LINQ से SQL SQL N + 1 क्वेरी करता है। यह वास्तव में अच्छा है। – Steven

+0

हाँ, ऐसा लगता है कि LINQ से SQL एक स्तर के गहरे (टेबलए और टेबलबी) तक नेस्टेड पैटर्न को संभालता है, लेकिन इससे भी गहरा होता है और आप टेबलसी में प्रत्येक आइटम के लिए एक अलग राउंड-ट्रिप के साथ समाप्त होते हैं। – StriplingWarrior

संबंधित मुद्दे