2012-02-08 3 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 row)?

Может ли кто-нибудь показать мне, как использовать 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; 
} 

Update Я вижу, что вы просили 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<> (используя поля вместо свойств и TypeInfo вместо TypeDescriptor). Он принимает весь объект DataTable как paramater и возвращает 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

Смежные вопросы