2012-12-31 37 views
6

मेरे पास कुछ कक्षाएं हैं जो प्रत्येक तालिका की पंक्तियों को DataGridView पर लोड करने के लिए डेटाबेस टेबल का प्रतिनिधित्व करती हैं, मेरे पास List<> फ़ंक्शन है जो लूप के अंदर उस तालिका से सभी पंक्तियां प्राप्त करता है।डायनामिक रूप से सूची प्रकार <> फ़ंक्शन निर्दिष्ट कैसे करें?

public List<class_Table1> list_rows_table1() 
{ 
    // class_Table1 contains each column of table as public property 
    List<class_Table1> myList = new List<class_Table1>(); 

    // sp_List_Rows: stored procedure that lists data 
    // from Table1 with some conditions or filters 
    Connection cnx = new Connection; 
    Command cmd = new Command(sp_List_Rows, cnx); 

    cnx.Open; 
    IDataReader dr = cmd.ExecuteReader(); 

    while (dr.Read()) 
    { 
     class_Table1 ct = new class_Table1(); 

     ct.ID = Convert.ToInt32(dr[ID_table1]); 
     ct.Name = dr[name_table1].ToString(); 
     //... all others wanted columns follow here 

     myList.Add(ct); 
    } 
    dr.Close(); 
    cnx.Close(); 

    // myList contains all wanted rows; from a Form fills a dataGridView 
    return myList(); 
} 

और अन्य तालिकाओं के लिए, कुछ अन्य कार्य: list_rows_table2, list_rows_table3 ... मेरा प्रश्न है: मैं एक ही List<> समारोह, के List<> लौटे जहां मैं गतिशील प्रकार निर्दिष्ट कर सकते हैं, या कैसे करने के लिए कैसे बना सकता हूँ कनवर्ट करने के लिए, उदाहरण के लिए List<object> से List<myClass> लौटने से पहले।

+3

यह मूल रूप से एक ओआरएम करता है। ओआरएम का उपयोग क्यों नहीं करें? एंटिटी फ्रेमवर्क बहुत अच्छी तरह से काम करता है, हम इसे एक बड़े LOB एप्लिकेशन में उपयोग कर रहे हैं जिसमें 400+ ग्राहक SAAS एप्लिकेशन चला रहे हैं (औसत 3+ कंप्यूटर प्रत्येक के साथ) और सर्वर पक्ष हमारे सर्वर में होस्ट किया जाता है। –

+2

एक [ValueInjecter] (http://valueinjecter.codeplex.com/) और विशेष रूप से [इस उदाहरण] (http://goo.gl/mD5OG) पर एक नज़र डालें, यह डोमेन ऑब्जेक्ट्स की सूची में डेटा रीडर को मानचित्र करता है जिस तरह से आप इसे करना चाहते हैं। Regardas और नया साल मुबारक हो! – Hugo

उत्तर

1

ओलिवियर का कार्यान्वयन अच्छा है। यह जेनरिक्स और इंटरफेस का उपयोग करता है जो प्रत्येक इकाई को FillFromDataReader() का अपना कार्यान्वयन देता है।

आप इसे आगे ले जा सकते हैं। सम्मेलन का उपयोग करके सभी डेटा हाइड्रेशन कोड को केंद्रीकृत और समेकित किया जा सकता है।

मुझे लगता है कि आपकी कक्षा संपत्ति के नाम और आपके कॉलम नाम समान हैं। यदि वे नहीं हैं तो संपत्ति कोड में उपनाम विशेषताओं को जोड़ने के लिए निम्न कोड बढ़ाया जा सकता है। कभी-कभी किसी वस्तु की वस्तु में अन्य मूल्यों से गणना की जाती है, इस संपत्ति को हाइड्रेटेड नहीं किया जा सकता है। एक अज्ञात विशेषता निम्न वर्ग में बनाई और कार्यान्वित की जा सकती है।

public class DataAccess 
{ 
    /// <summary> 
    /// Hydrates the collection of the type passes in. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="sql">The SQL.</param> 
    /// <param name="connection">The connection.</param> 
    /// <returns>List{``0}.</returns> 
    public List<T> List<T>(string sql, string connection) where T: new() 
    { 
     List<T> items = new List<T>(); 

     using (SqlCommand command = new SqlCommand(sql, new SqlConnection(connection))) 
     { 
      string[] columns = GetColumnsNames<T>(); 
      var reader = command.ExecuteReader(CommandBehavior.CloseConnection); 

      while (reader.Read()) 
      { 
       T item = new T(); 

       foreach (var column in columns) 
       { 
        object val = reader.GetValue(reader.GetOrdinal(column)); 
        SetValue(item, val, column); 
       } 

       items.Add(item); 
      } 

      command.Connection.Close(); 

     } 

     return items; 
    } 

    /// <summary> 
    /// Sets the value. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="item">The item.</param> 
    /// <param name="value">The value.</param> 
    /// <param name="column">The column.</param> 
    private void SetValue<T>(T item, object value, string column) 
    { 
     var property = item.GetType().GetProperty(column); 
     property.SetValue(item, value, null); 
    } 

    /// <summary> 
    /// Gets the columns names. 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <returns>System.String[][].</returns> 
    private string[] GetColumnsNames<T>() where T : new() 
    { 
     T item = new T(); 

     return (from i in item.GetType().GetProperties() 
       select i.Name).ToArray(); 
    } 
} 

उपरोक्त कोड में कुछ चेतावनी हैं। डीबीएनयूल्स और नीलबल प्रकार विशेष मामले हैं और उन्हें निपटने के लिए कस्टम कोड की आवश्यकता होगी। मैं आमतौर पर डीबीएनयूएल को शून्य में परिवर्तित करता हूं। मैंने कभी ऐसे मामले में भाग नहीं लिया है जहां मुझे दोनों के बीच अलग अंतर करने की जरूरत है। नूलल्बे प्रकारों के लिए, बस नलबल प्रकार का पता लगाएं और तदनुसार कोड को संभाल लें।

एक ओआरएम डाटा एक्सेस से निपटने के अधिकांश सिरदर्द को हटा देगा। नकारात्मकता, कई बार आप डीटीओ और डेटाबेस स्कीमा के साथ मिलकर हैं। बेशक इस मुद्दे को abstractions का उपयोग करके निहित किया जा सकता है।कई कंपनियां अभी भी सख्ती से संग्रहित प्रक्रियाओं का उपयोग करती हैं, अधिकतर ओआरएम तब गिर जाते हैं जब उन्हें संग्रहीत प्रक्रियाओं का उपभोग करने के लिए मजबूर किया जाता है। वे सिर्फ संग्रहित प्रक्रियाओं के साथ काम करने के लिए डिज़ाइन नहीं किए गए हैं।

मैंने "Hypersonic" नामक डेटा एक्सेस फ्रेमवर्क लिखा था। यह गिटहब पर है, यह विशेष रूप से संग्रहित प्रक्रियाओं के साथ काम करने के लिए डिज़ाइन किया गया है। उपर्युक्त कोड इसका एक हल्का कार्यान्वयन है।

+1

[हाइड्रेशन] (http://stackoverflow.com/a/4929478/880990) –

+1

इस उत्तर में कोड के साथ समस्याएं: 1. SQLConnection को "उपयोग" या "कोशिश/अंत में" में ठीक तरह से निपटाया नहीं जा रहा है। 2. यह पाठक [कॉलम] के बजाय "reader.GetValue (reader.GetOrdinal (कॉलम)) का उपयोग क्यों कर रहा है? 3. SetValue मान सेट करने के लिए प्रतिबिंब का उपयोग कर रहा है। यदि आपके पास बड़ी संख्या में आइटम हैं, तो यह बहुत धीमा होगा। 4. GetColumnNames को केवल नए टी()। GetType() के बजाय टाइपऑफ (टी) का उपयोग करना चाहिए। –

7

आप एक अंतरफलक है कि अपने सभी डेटा वर्गों को लागू

public interface IData 
{ 
    void FillFromReader(IDataReader dr); 
} 

फिर इस

public List<T> GetList<T>(string sqlText) 
    where T : IData, new() 
{ 
    List<T> myList = new List<T>(); 

    using (Connection cnx = new Connection(connString)) 
    using (Command cmd = new Command(sqlText, cnx)) { 
     cnx.Open(); 
     using (IDataReader dr = cmd.ExecuteReader()) { 
      while (dr.Read()) 
      { 
       T item = new T(); 
       item.FillFromReader(dr); 
       myList.Add(item); 
      } 
     } 
    } 
    return myList(); 
} 

की तरह अपने विधि में परिवर्तन करना होगा तो मूल रूप से प्रत्येक वर्ग के लिए अपने स्वयं के क्षेत्रों को भरने के लिए जिम्मेदार होगा हो सकता था।

सामान्य प्रकार पैरामीटर के लिए बाधा where T : IData, new() महत्वपूर्ण है। यह विधि बताता है कि T को IData इंटरफ़ेस को लागू करना होगा। कास्टिंग किए बिना विधि FillFromReader पर कॉल करने में सक्षम होने के लिए यह आवश्यक है। डेटा कक्षाएं एक डिफ़ॉल्ट निर्माता होना चाहिए (इस new() द्वारा निर्दिष्ट किया जाता है। इसमें आपके new T() के साथ एक दृष्टांत के लिए सक्षम बनाता है।


मैं कनेक्शन, कमान और using बयानों के साथ डेटा रीडर का उपयोग कर कोड को घेर लिया। using बयान बंद कर देता है और संसाधनों ब्लॉक के अंत में स्वचालित रूप से छोड़ देता है और सुनिश्चित करता है कि इस होता है एक अपवाद फेंका जाना चाहिए, भले ही या बयान ब्लॉक उदाहरण के लिए एक वापसी-बयान के साथ समय से पहले ही छोड़ दिया जाना चाहिए।

using Statement (C# Reference)

देखें