2012-03-30 3 views
6

Sto usando .NET 3.5 e ho bisogno di convertire il sotto selezionare il nuovo risultato in un DataTable. C'è qualcosa di costruito per questo o qualcuno sa di un metodo che può farlo?Converti selezionare nuovo in 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

Cosa stai facendo che hai bisogno di un DataTable come risultato? – dwerner

+0

Sto tentando di esportare in CSV e ho già metodi che possono esportare DataTable/Set in CSV. – TruMan1

+1

Penso che tu stia cercando un linguaggio di programmazione dinamico in uno (principalmente) tipizzato staticamente. Se sei molto legato a questi DataTable alle routine di esportazione csv, allora temo che il tuo percorso possa essere un passo solo per te. Gli oggetti anonimi sono una funzionalità linguistica abbastanza nuova e DataRow/DataTable/DataSet non sono stati progettati per loro. – dwerner

risposta

7

facile e semplice cosa da fare è usare la riflessione:

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); 
} 

Il codice potrebbe non funzionare in anticipo ma dovrebbe darvi un'idea generale. Innanzitutto, ottieni propertyInfos dal tipo anonimo e utilizza questi metadati per creare uno schema datatable (colonne di riempimento). Quindi usi queste informazioni per ottenere valori da ogni oggetto.

+0

Ma Reflection sta andando a colpire le prestazioni ogni volta che il codice viene eseguito.Preferirei aver specificato le proprietà manualmente nel codice. – Ramesh

+1

Bene, poiché OP ha 30+ proprietà e probabilmente più di 1 caso d'uso, sarebbe controproducente specificarlo manualmente. Inoltre, ci sono trucchi per rendere la riflessione più produttiva (come l'uso del riflesso solo una volta e la generazione di un metodo di mappatura dinamico che salva in memoria per usi futuri di conversione dell'oggetto o libreria di rifrazione veloce). –

+0

Guarda la mia soluzione generica senza usare Reflection usando invece alberi di espressioni. Prelevato del codice dalla tua soluzione – Ramesh

0

C'è uno CopyToDataTable extension method che lo fa per voi. Vive in System.Data.DataSetExtensions.dll

+0

Speravo di poter utilizzare questo, ma ottengo un errore di compilazione: Errore Il tipo 'AnonymousType # 1' non può essere utilizzato come tipo parametro 'T' nel tipo generico o nel metodo 'System.Data.DataTableExtensions.CopyToDataTable (System.Collections.Generic.IEnumerable )'. Non esiste alcuna conversione implicita del riferimento da "AnonymousType # 1" a "System.Data.DataRow". – TruMan1

+0

Sì, questo non è un metodo utile per copiare da oggetti, anonimi o meno, a meno che non scendano da DataRow. – dwerner

0

Prova questo:

// 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. 

Articolo originale di codice di esempio: Converting Anonymous type generated by LINQ to a DataTable type

EDIT: Pseudocodice Generico:

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

In realtà ho oltre 30+ proprietà e voglio che questo sia generico. C'è un altro modo oltre a codificare le colonne? Potrei anche creare una vera classe invece di usarne una anonima. – TruMan1

+0

Controlla la mia modifica. Sto ancora pensando a come rendere la 2a parte generica. –

0

È possibile convertire il risultato elenco per DataTable dalla funzione di sotto

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

Ecco una soluzione generica senza riflettere sulle proprietà. Avere un metodo di estensione come sotto

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; 
    } 

E Invocare lo stesso utilizzando dove i parametri saranno il nome della colonna nell'ordine desiderato.

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

La variabile non sembra mai cambiare? – 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; 
     } 

dopo aver fatto la vostra selezione nuova possibile per .ToList().ListToDataTable(). Utilizza la riflessione ComponentModel ed è (per via termica) più veloce di System.Reflection.