2012-03-30 16 views
6

Używam .NET 3.5 i muszę przekonwertować poniższy wynik, wybierz nowy wynik w DataTable. Czy jest coś wbudowanego w to lub ktoś wie o metodzie, która może to zrobić?Konwertuj wybierz nowy do DataTable?

var contentList = (from item in this.GetData().Cast<IContent>() 
        select new 
        { 
         Title = item.GetMetaData("Title"), 
         Street = item.GetMetaData("Street"), 
         City = item.GetMetaData("City"), 
         Country = item.GetMetaData("Country") 
        }); 
+0

Co robisz, że potrzebujesz DataTable jako wynik? – dwerner

+0

Próbuję wyeksportować do pliku CSV i już mam metody, które mogą eksportować DataTable/Set do CSV. – TruMan1

+1

Myślę, że szukasz dynamicznego języka programowania w (głównie) statycznie napisanym typie. Jeśli jesteś bardzo przywiązany do tych procedur DataTable do eksportu CSV, obawiam się, że twoja ścieżka może być tylko jednym bieżnikiem. Obiekty anonimowe to dość nowa funkcja językowa, a DataRow/DataTable/DataSet nie zostały zaprojektowane z myślą o nich. – dwerner

Odpowiedz

7

Łatwa i prosta rzeczą do zrobienia jest użycie refleksji:

var records = (from item in this.GetData().Cast<IContent>() 
          select new 
          { 
           Title = "1", 
           Street = "2", 
           City = "3", 
           Country = "4" 
          }); 
var firstRecord = records.First(); 
if (firstRecord == null) 
    return; 

var infos = firstRecord.GetType().GetProperties(); 
DataTable table = new DataTable(); 
foreach (var info in infos) { 
    DataColumn column = new DataColumn(info.Name, info.PropertyType); 
    table.Columns.Add(column); 
} 

foreach (var record in records) { 
    DataRow row = table.NewRow(); 
    for (int i = 0; i < table.Columns.Count; i++) 
     row[i] = infos[i].GetValue(record); 
    table.Rows.Add(row); 
} 

Kod może nie działać z góry, ale powinien dać ci ogólny pogląd. Najpierw otrzymujesz propertyInfos z anonimowego typu i używasz tych metadanych do tworzenia schematów danych (wypełnij kolumny). Następnie używasz tych informacji, aby uzyskać wartości z każdego obiektu.

+0

Ale Reflection osiągnie wydajność za każdym razem, gdy kod będzie działał.Wolałbym ręcznie określić właściwości w kodzie. – Ramesh

+1

Cóż, ponieważ OP ma ponad 30 właściwości i prawdopodobnie więcej niż jeden przypadek użycia, ręczne określenie go byłoby nieproduktywne. Istnieją również sztuczki, które sprawiają, że odbicie jest bardziej produktywne (np. Za pomocą odbicia tylko jeden raz i generowanie dynamicznej metody mapowania zapisanej w pamięci dla przyszłych zastosowań konwersji obiektów lub biblioteki fastreflection). –

+0

Spójrz na moje ogólne rozwiązanie bez użycia refleksji zamiast używania drzewek wyrażeń. Pożyczono kod z twojego rozwiązania. – Ramesh

0

Istnieje CopyToDataTable extension method, który robi to za Ciebie. Żyje w System.Data.DataSetExtensions.dll

+0

Miałem nadzieję, że mogę tego użyć, ale dostaję błąd kompilacji: Błąd Typ 'AnonymousType # 1' nie może być użyty jako typ parametr "T" w typie ogólnym lub metodzie "System.Data.DataTableExtensions.CopyToDataTable (System.Collections.Generic.IE liczenie )". Nie ma żadnej niejawnej konwersji odniesienia z "AnonymousType # 1" do "System.Data.DataRow". – TruMan1

+0

Tak, to nie jest użyteczna metoda kopiowania z obiektów, anonimowych lub innych, chyba że pochodzą one od DataRow. – dwerner

0

Spróbuj tego:

// Create your datatable. 

DataTable dt = new DataTable(); 
dt.Columns.Add("Title", typeof(string)); 
dt.Columns.Add("Street", typeof(double)); 


// get a list of object arrays corresponding 
// to the objects listed in the columns 
// in the datatable above. 
var result = from item in in this.GetData().Cast<IContent>()    
      select dt.LoadDataRow(
       new object[] { Title = item.GetMetaData("Title"), 
           Street = item.GetMetaData("Street"), 
       }, 
       false); 


// the end result will be a set of DataRow objects that have been 
// loaded into the DataTable. 

oryginalny artykuł dla próbki kodu: Converting Anonymous type generated by LINQ to a DataTable type

EDIT: Generic pseudokod:

void LinqToDatatable(string[] columns, Type[] datatypes, linqSource) 
{ 
    for loop 
    { 
     dt.columns.add(columns[i], datatypes[i]); 
    } 

//Still thinking how to make this generic.. 
var result = from item in in this.GetData().Cast<IContent>()    
      select dt.LoadDataRow(
       new object[] { string[0] = item.GetMetaData[string[0]], 
           string[1] = item.GetMetaData[srring[1] 
       }, 
       false); 


} 
+0

Rzeczywiście mam ponad 30 obiektów i chcę, żeby były ogólne. Czy istnieje inny sposób poza twardym kodowaniem kolumn? Równie dobrze mógłbym stworzyć prawdziwą klasę zamiast używać anonimowej. – TruMan1

+0

Sprawdź moją zmianę. Nadal myślę o tym, jak uczynić drugą część generyczną. –

0

można przekonwertować listę wynik DataTable w poniższej funkcji

public static DataTable ToDataTable<T>(IEnumerable<T> values) 
    { 
     DataTable table = new DataTable(); 

     foreach (T value in values) 
     { 
      if (table.Columns.Count == 0) 
      { 
       foreach (var p in value.GetType().GetProperties()) 
       { 
        table.Columns.Add(p.Name); 
       } 
      } 

      DataRow dr = table.NewRow(); 
      foreach (var p in value.GetType().GetProperties()) 
      { 
       dr[p.Name] = p.GetValue(value, null) + ""; 

      } 
      table.Rows.Add(dr); 
     } 

     return table; 
    } 
3

Oto jedno rozwiązanie generic bez Zastanawiając się nad właściwościami. Mają metodę rozszerzenia jak poniżej

public static DataTable ConvertToDataTable<TSource>(this IEnumerable<TSource> 
        records, params Expression<Func<TSource, object>>[] columns) 
    { 
     var firstRecord = records.First(); 
     if (firstRecord == null) 
      return null; 

     DataTable table = new DataTable(); 

     List<Func<TSource, object>> functions = new List<Func<TSource, object>>(); 
     foreach (var col in columns) 
     { 
      DataColumn column = new DataColumn(); 
      column.Caption = (col.Body as MemberExpression).Member.Name; 
      var function = col.Compile(); 
      column.DataType = function(firstRecord).GetType(); 
      functions.Add(function); 
      table.Columns.Add(column); 
     } 

     foreach (var record in records) 
     { 
      DataRow row = table.NewRow(); 
      int i = 0; 
      foreach (var function in functions) 
      { 
       row[i++] = function((record)); 
      } 
      table.Rows.Add(row); 
     } 
     return table; 
    } 

i wywoływać takie same parametry używając gdzie będzie nazwa kolumny w kolejności.

var table = records.ConvertToDataTable(
             item => item.Title, 
             item => item.Street, 
             item => item.City 
            ); 
+0

Zmienna i nigdy się nie zmienia? – NetMage

0
public static DataTable ListToDataTable<T>(this IList<T> data) 
     { 
      DataTable dt = new DataTable(); 
      PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); 
      for (int i = 0; i < props.Count; i++) 
      { 
       PropertyDescriptor prop = props[i]; 
       dt.Columns.Add(prop.Name, prop.PropertyType); 
      } 
      object[] values = new object[props.Count]; 
      foreach (T t in data) 
      { 
       for (int i = 0; i < values.Length; i++) 
       { 
        values[i] = props[i].GetValue(t); 
       } 
       dt.Rows.Add(values); 
      } 
      return dt; 
     } 

Po zrobić swoją wybierz nowy można do .ToList().ListToDataTable(). Wykorzystuje odbicie ComponentModel i jest (teoretycznie) szybsze niż System.Reflection.