2012-02-08 62 views
2

我正在嘗試編寫一個將DataTable轉換爲強類型對象列表的泛型方法。將DataTable轉換爲強類型對象列表

是我與迄今使用的代碼...

public List<T> ImportTable<T>(String fileName, String table) 
{ 
    //Establish Connection to Access Database File 
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;"); 

    var tableData = new List<T>(); 

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows) 
    { 
     tableData.Add(ConvertRowToType<T>(row)); 
    } 

    return tableData; 
} 

public T ConvertRowToType<T>(DataRow row) 
{ 
    //??? What is the best thing to do here ???   
} 

我不迷戀上這個代碼,如果任何人的建議,將需要改變它。

所以我們可以說我調用該函數傳入型...

public class mdbConcern 
{ 
    public Int32 ConcernId { get; set; } 
    public String Concern { get; set; } 
} 

和數據回來DataTable中的樣子......

ConcernID Concern 
1   Law and Ethics 
2   Mail 
3   Business English 
...  ... 

什麼是最好的實現ConvertRowToType(DataRow行)方法的方法?

有人能告訴我如何使用Func作爲參數之一,所以我可以傳遞一些映射信息?

+2

可能的重複http://stackoverflow.com/q/441023/490018,http://stackoverflow.com/q/5856634/490018。 – 2012-02-08 18:48:14

回答

6

我想到一個extension method是最好的一段路要走:

public static class Helper 
{ 
    public static T ToType<T>(this DataRow row) where T : new() 
    { 
     T obj = new T(); 
     var props = TypeDescriptor.GetProperties(obj); 
     foreach (PropertyDescriptor prop in props) 
     { 
      if(row.Table.Columns.IndexOf(prop.Name) >= 0 
       && row[prop.Name].GetType() == prop.PropertyType) 
      { 
       prop.SetValue(obj, row[prop.Name]); 
      } 
     } 
     return obj; 
    } 
} 

用法:

public List<T> ImportTable<T>(String fileName, String table) 
{ 
    //Establish Connection to Access Database File 
    var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;"); 

    var tableData = new List<T>(); 

    foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows) 
    { 
     tableData.Add(row.ToType<T>()); 
    } 

    return tableData; 
} 

更新我看到你問了Func,將提供映射。我不知道你設想到底是什麼,但這裏是我想出了一個方法:

public class mdbConcern 
{ 
    public Int32 ConcernId { get; set; } 
    public String Concern { get; set; } 

    public static PropertyDescriptor Mapping(string name) 
    { 
     PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(mdbConcern)); 
     switch (name) 
     { 
      case "Concern_Id": 
       return props.GetByName("ConcernId"); 
      case "Concern": 
       return props.GetByName("Concern"); 
      default: 
       return null; 
     } 
    } 
} 

public static class Helper 
{ 
    public static T ToType<T>(this DataRow row, Func<string, PropertyDescriptor> mapping) 
     where T : new() 
    { 
     T obj = new T();   
     foreach (DataColumn col in row.Table.Columns) 
     { 
      var prop = mapping(col.ColumnName); 
      if(prop != null) 
       prop.SetValue(obj, row[col]); 
     } 
     return obj; 
    } 
} 

用法:

foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows) 
{ 
    tableData.Add(row.ToType<mdbConcern>(mdbConcern.Mapping)); 
} 

下面是一個使用該類型的屬性的屬性存儲其映射的版本。我認爲這是更自然的解決方案:

[AttributeUsage(AttributeTargets.Property)] 
public class ColumnMappingAttribute : Attribute 
{ 
    public string Name { get; set; } 
    public ColumnMappingAttribute(string name) 
    { 
     Name = name; 
    } 
} 
public class mdbConcern 
{ 
    ColumnMapping("Concern_Id")] 
    public Int32 ConcernId { get; set; } 
    ColumnMapping("Concern")] 
    public String Concern { get; set; } 
} 

public static class Helper 
{ 
    public static T ToType<T>(this DataRow row) where T : new() 
    { 
     T obj = new T(); 
     var props = TypeDescriptor.GetProperties(obj); 
     foreach (PropertyDescriptor prop in props) 
     { 
      var columnMapping = prop.Attributes.OfType<ColumnMappingAttribute>().FirstOrDefault(); 

      if(columnMapping != null) 
      { 
       if(row.Table.Columns.IndexOf(columnMapping.Name) >= 0 
        && row[columnMapping.Name].GetType() == prop.PropertyType) 
       {    
        prop.SetValue(obj, row[columnMapping.Name]); 
       } 
      } 
     } 
     return obj; 
    } 
} 
+0

這是一個整齊的代碼示例,但不幸的是我不認爲它會適用於我的情況。我從中抽取的數據源是一箇舊的Access數據庫,整個模式中的許多列名都有空格。因此,我的對象上的屬性並不都與它們應該對應的表中的列名匹配。 – jdavis 2012-02-08 20:36:17

+0

@jdavis我用我認爲更像你要找的東西的方法更新了我的答案。讓我知道它是否有幫助。 – Sorax 2012-02-10 16:55:08

0

除@Sorax答案。我增強了ToType方法來支持Nullable<>類型成員(使用字段而不是屬性,而TypeInfo而不是TypeDescriptor)。它將整個DataTable對象作爲參數並返回IList

protected IList<TResult> TableToList<TResult>(DataTable table) where TResult : new() 
    { 
     var result = new List<TResult>(table.Rows.Count); 

     var fields = typeof(TResult).GetTypeInfo().DeclaredFields; 

     TResult obj; 
     Object colVal; 
     var columns = table.Columns; 
     var nullableTypeDefinition = typeof(Nullable<>); 
     var dbNullType = typeof(DBNull); 
     Type[] genericArguments; 

     foreach (DataRow row in table.Rows) 
     { 
      obj = new TResult(); 

      foreach (var f in fields) 
      { 
       if (columns.Contains(f.Name)) 
       { 
        colVal = row[f.Name]; 
        if (colVal.GetType() == f.FieldType) 
        { 
         f.SetValue(obj, colVal); 
        } 
        else if (colVal.GetType() != dbNullType && f.FieldType.IsGenericType && 
          f.FieldType.GetGenericTypeDefinition() == nullableTypeDefinition) 
        { 
          genericArguments = f.FieldType.GetGenericArguments(); 

          if (genericArguments.Length > 0 && genericArguments[0] == colVal.GetType()) 
          { 
           f.SetValue(obj, colVal); 
          } 
        } 
       } 
      } 

      result.Add(obj); 
     } 

     return result; 
    } 
+0

我在代碼的以下行中收到錯誤 var fields = typeof(TResult).GetTypeInfo() 無法在此處訪問私有方法GetTypeInfo – TWilly 2015-10-23 21:00:27

相關問題