2012-12-31 33 views
6

我有一些代表數據庫表的類,爲了加載每個表的行在DataGridView,我有一個List<>函數,在循環內獲取該表中的所有行。如何動態指定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

這基本上是ORM的功能。爲什麼不使用ORM?實體框架運行得非常好,我們將它用在一個大型的LOB應用程序中,有400多個客戶運行一個SAAS應用程序(每個平臺有3個以上的計算機),服務器端託管在我們的服務器中。 –

+2

看一看[ValueInjecter](http://valueinjecter.codeplex.com/),特別是[本示例](http://goo.gl/mD5OG),它會將數據讀取器映射到域對象列表就像你想要做的那樣。敬意和新年快樂! – 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(); 
    } 
} 

在上面的代碼中有幾個注意事項。 DBNulls和Nullable類型是特例,需要自定義代碼來處理它們。我通常將DBNull的轉換爲null。我從來沒有遇到過需要區分兩者不同的情況。對於Nullalbe類型,只需檢測Nullable類型並相應地處理代碼。

ORM將消除處理數據訪問的許多麻煩。不利的一面是,很多時候你會聯繫到DTO和數據庫模式。當然這個問題可以通過使用抽象來包含。許多公司仍然使用嚴格的存儲過程,大多數ORM在被迫使用存儲過程時會崩潰。它們只是沒有設計用於存儲過程。

我寫了一個名爲「Hypersonic」的數據訪問框架。它在GitHub上,它專門用於存儲過程。上面的代碼是它的一個輕量級實現。

+1

[水化](http://stackoverflow.com/a/4929478/880990) –

+1

此答案中的代碼問題:1.在「使用」或「嘗試/最終」中SQLConnection未正確放置。 2.爲什麼它使用「reader.GetValue(reader.GetOrdinal(column))」而不是reader [column]? 3. SetValue使用反射來設置值。如果你有大量的項目,這將是非常緩慢的。 4. GetColumnNames應該使用typeof(T)而不是新的T()。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)

相關問題