2011-03-02 57 views
1

我正在用訪問SQLCE數據庫的方法創建一個databasehelper類。我想使用相同的方法來讀取使用包含與不同表中的字段匹配的屬性的不同類的行。要使用的類是在運行時確定的,我希望將對象從類中的對象傳遞給方法,並獲取屬性名並使用它們讀取數據庫。將非常方便,因爲我可以將它用於我的所有(SQLCE-)數據庫。如何將包含一些屬性的類型爲custom-class的未知對象列表傳遞給方法?

(我更新了錯誤代碼,以便在這裏提供的解決方案)

#region ReadData 
    ///---------------------------------------------------------------------- 
    /// <summary> 
    /// Reads datarows from database and adds them to list. 
    /// </summary> 
    /// <param name="data">List containing objects with properties.</param> 
    /// <param name="table">Table in database.</param> 
    /// <param name="search">Substring of SQL-statement that follows 'WHERE'.</param> 
    /// <param name="connect">Connectionstring.</param> 
    /// <returns>true if successfull</returns> 
    ///---------------------------------------------------------------------- 
    public static bool ReadData<T>(List<T> data, string table, string search, string connect) where T : class, new() 
    { 
     // Return if input id missing 
     if (data == null || table == "" || connect == "") return false; 

     // retrieve properties from Data 
     PropertyInfo[] propinf = typeof(T).GetProperties(); 

     // Create string with SQL-statement 
     string fields = ""; 
     // retrieve fields from propinf 
     foreach (PropertyInfo p in propinf) 
     { 
      fields += fields == "" ? p.Name : ", " + p.Name; 
     } 
     // create SQL SELECT statement with properties and search 
     string sql = "SELECT " + fields + " FROM " + table; 
     sql += search == "" ? "" : " WHERE " + search; 

     // Instantiate and open database 
     SqlCeConnection cn = new SqlCeConnection(connect); 
     if (cn.State == ConnectionState.Closed) 
     cn.Open(); 
     data.Clear(); // just in case 
     try 
     { 
      SqlCeCommand cmd = new SqlCeCommand(sql, cn); 
      cmd.CommandType = CommandType.Text; 
      SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable); 
      if (rs.HasRows) // Only if database is not empty 
      { 
       while (rs.Read()) // read database 
       { 
        // instantiate single item of list Data 
        var dataitem = new T(); 
        int ordinal = 0; 
        foreach (PropertyInfo p in propinf) 
        { 
         // read database and 
         PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name); 
         ordinal = rs.GetOrdinal(p.Name); 
         singlepropinf.SetValue(dataitem, rs.GetValue(ordinal), null); // fill data item 
        } 
        data.Add(dataitem); // and add it to data. 
       } 
      } 
      else 
      { 
       MessageBox.Show("No records matching '" + search + "'!"); 
       return false; 
      } 
     } 
     catch (SqlCeException sqlexception) 
     { 
      MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return false; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return false; 
     } 
     finally 
     { 
      cn.Close(); 
     } 
     return true; 
    } 
    #endregion 

我有兩個問題:

1)我怎麼通過這個列表與未知類型?我到目前爲止發現的答案並沒有幫助我解決這個問題。

2)我如何實例化未知類型的對象(在編譯時)以將其添加到列表而不會導致編譯錯誤?

非常感謝!

+0

是否應該在While(rs.Read())循環中移動_dataItem的創建?否則每次都會插入相同的_dataItem。也許我錯過了什麼? Regards Morten – Morten 2011-03-03 00:10:14

+0

似乎對我來說。謝謝! – 2011-03-03 00:28:07

+0

剛編輯的代碼 – 2011-03-03 00:31:06

回答

5

1:未知類型的列表,可非一般IList,或ArrayList,或List<object>

2:Activator.CreateInstance(type)

或者,看看寫一個通用的方法,最好是這樣的:

ReadData<T>(List<T> data, ...) where T : class, new() 

並使用new T()來創建新項目和typeof(T)來談談Type。使用通用方法,調用者提供T - 通常是隱含的。請注意,在您的示例中不需要ref

+0

太棒了!它編譯沒有問題(多麼浪費我以前的時間...)。我明天會測試它。 (事實上​​,我發現了''之前的東西,但並沒有真正理解如何使用它。這對我來說是一個全新的概念,曾經與Turbo-pascal 5.5和VBA一起工作......) – 2011-03-02 23:11:37

0

下面的代碼更新。它越來越接近最終,並已在各種不同的情況下進行測試。理想情況下,使用反射的迭代必須用性能不太強烈的東西來代替,但只要數據庫操作比較耗時,我想在現實生活中它並不重要。我已經很滿意了。

#region Read(Like)Data 
    public static int ReadData<T>(List<T> data, string table, T search, string connect) where T : class, new() 
    { 
     return BaseRead(data, table, search, connect, "="); 
    } 
    public static int ReadLikeData<T>(List<T> data, string table, T search, string connect) where T : class, new() 
    { 
     return BaseRead(data, table, search, connect, "LIKE"); 
    } 
    ///---------------------------------------------------------------------- 
    /// <summary> 
    /// Reads datarows from database and adds them to list containing objects of type T. 
    /// Note that the properties of T should match the fields of the database table. 
    /// </summary> 
    /// <param name="data">List containing objects of type T with properties matching fields in table.</param> 
    /// <param name="table">Table in database.</param> 
    /// <param name="search">Object of type T with (some) properties containing search constraints, 
    /// others should be null. Unused DateTime should be 1800-01-01.</param> 
    /// <param name="connect">Connectionstring.</param> 
    /// <returns>-1 if exception was thrown or the number of records (objects of type T) otherwise</returns> 
    ///---------------------------------------------------------------------- 
    private static int BaseRead<T>(List<T> data, string table, T search, string connect, string comparer) where T : class, new() 
    { 
     // Abort if insufficient arguments 
     if (data == null || table == "" || connect == "") return 0; 
     // Make sure List<T> data is empty 
     data.Clear(); 
     // Retrieve properties from object of type T 
     PropertyInfo[] propinfs = typeof(T).GetProperties(); 

     // ----------------------------------------- 
     // Create string that contains SQL-statement 
     // ----------------------------------------- 
     string fields = ""; string wherestr = ""; 
     // Retrieve fields from propinf 
     foreach (PropertyInfo p in propinfs) 
     { 
      fields += fields == "" ? p.Name : ", " + p.Name; 
      dynamic propvalue = p.GetValue(search, null); 
      // Solutions for properties of type DateTime 
      long dateticks = 0; DateTime dt = new DateTime(); 
      Type type = propvalue != null ? propvalue.GetType() : null; 
      if (propvalue != null && propvalue.GetType() == dt.GetType()) 
      { 
       dt = propvalue; 
       dateticks = dt.Ticks; 
      } 
      // DateTime 1800-01-01 equals null (hey, it's better than nothing...) 
      if (propvalue != null && dt != DateTimeNull) 
       wherestr += wherestr == "" ? p.Name + " " + comparer + " @" + p.Name.ToLower() 
        : " AND " + p.Name + " " + comparer + " @" + p.Name.ToLower(); 
     } 
     // Create SQL SELECT statement with properties and search 
     string sql = "SELECT " + fields + " FROM " + table; 
     sql += wherestr == "" ? "" : " WHERE " + wherestr; 

     // ------------------- 
     // Database operations 
     // ------------------- 
     SqlCeConnection cn = new SqlCeConnection(connect); 
     if (cn.State == ConnectionState.Closed) cn.Open(); 
     try 
     { 
      SqlCeCommand cmd = new SqlCeCommand(sql, cn); 
      cmd.CommandType = CommandType.Text; 
      // Add propertyvalues to WHERE-statement using reflection 
      foreach (PropertyInfo p in propinfs) 
      { 
       dynamic propvalue = p.GetValue(search, null); 
       // Except for DateTime values 1800-01-01 (defined as null) 
       if (propvalue != null && !(propvalue.GetType() is DateTime && propvalue != DateTimeNull)) 
       { 
        if (comparer == "LIKE") propvalue = "%" + propvalue + "%"; 
        cmd.Parameters.AddWithValue("@" + p.Name.ToLower(), propvalue); 
       } 
      } 
      SqlCeResultSet rs = cmd.ExecuteResultSet(ResultSetOptions.Scrollable); 
      if (rs.HasRows) // Only if database is not empty 
      { 
       while (rs.Read()) // Read next row in database 
       { 
        // Instantiate single item of List data 
        var dataitem = new T(); // Object to put the field-values in 
        foreach (PropertyInfo p in propinfs) 
        { 
         // Read database fields using reflection 
         PropertyInfo singlepropinf = typeof(T).GetProperty(p.Name); 
         int ordinal = rs.GetOrdinal(p.Name); 
         dynamic result = rs.GetValue(ordinal); 
         // Conversion to null in case field is DBNull 
         if (result is DBNull) 
         { 
          if (singlepropinf.PropertyType.Equals(typeof(DateTime))) 
          { 
           singlepropinf.SetValue(dataitem, DateTimeNull, null); // Fill data item with datetimenull 
          } 
          else 
          { 
           singlepropinf.SetValue(dataitem, null, null); // Fill data item with null 
          } 
         } 
         else 
         { 
          singlepropinf.SetValue(dataitem, result, null); // Or fill data item with value 
         } 
        } 
        data.Add(dataitem); // And add the record to List<T> data. 
       } 
      } 
      else 
      { 
       //MessageBox.Show("No records matching '" + wherestr + "'!"); 
       return 0; 
      } 
     } 
     catch (SqlCeException sqlexception) 
     { 
      MessageBox.Show(sqlexception.Message, "SQL-error.", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return -1; 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message, "Error.", MessageBoxButtons.OK, MessageBoxIcon.Error); 
      return -1; 
     } 
     finally 
     { 
      cn.Close(); 
     } 
     // Return number of objects (should equal number of retrieved records) 
     return data.Count(); 
    } 
    #endregion 
相關問題