2012-02-08 9 views
2

DataTableを厳密に型指定されたオブジェクトのリストに変換する汎用メソッドを記述しようとしています。DataTableを厳密に型指定されたオブジェクトのリストに変換する

私はこれまで、ISで働いているのコード...

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行)メソッドを実装する方法は?

私はいくつかのマッピング情報を渡すことができるので、誰かが私にパラメータの1つとしてFuncを使う方法を教えてもらえますか?

+2

可能な複製http://stackoverflow.com/q/441023/490018、http://stackoverflow.com/q/5856634/490018。 –

答えて

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

+0

@jdavis私はあなたが探しているものに近いと思う方法で私の答えを更新しました。それが役に立ったら教えてください。 – Sorax

0

@Sorax答えに加えて。私はToTypeメソッドを拡張し、Nullable<>タイプのメンバーをサポートしました(プロパティの代わりにフィールドを使用し、の代わりに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

関連する問題