2012-03-30 28 views
6

Estoy usando .NET 3.5 y necesito convertir el siguiente, seleccione un nuevo resultado en una DataTable. ¿Hay algo incorporado para esto o alguien sabe de un método que puede hacer esto?¿Convertir selecciones nuevas a 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

¿Qué estás haciendo que necesitas una DataTable como resultado? – dwerner

+0

Estoy intentando exportar a CSV y ya tengo métodos que pueden exportar DataTable/Set a CSV. – TruMan1

+1

Creo que estás buscando un lenguaje de programación dinámico en uno (la mayoría) tipado estáticamente. Si estás muy apegado a esas rutinas de exportación de DataTable a csv, entonces me temo que tu camino podría ser solo una huella. Los objetos anónimos son una característica del lenguaje bastante nueva, y DataRow/DataTable/DataSet no se diseñaron teniendo en cuenta estos aspectos. – dwerner

Respuesta

7

fácil y sencillo Lo que hay que hacer es utilizar la reflexión:

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

Es posible que el código no funcione correctamente, pero debe darle una idea general. Primero, obtienes propertyInfos del tipo anónimo y usas estos metadatos para crear un esquema de tablas de datos (columnas de relleno). Luego, usa esas informaciones para obtener valores de cada objeto.

+0

Pero la reflexión afectará el rendimiento cada vez que se ejecuta el código.Preferiría haber especificado las propiedades manualmente en el código. – Ramesh

+1

Bueno, dado que OP tiene más de 30 propiedades y probablemente más de 1 caso de uso, sería contraproducente especificarlo manualmente. Además, existen trucos para hacer que la reflexión sea más productiva (como usar el reflejo solo una vez y generar el método de mapeo dinámico para guardarlo en memoria para futuros usos de conversión de objetos o para la biblioteca de revisión rápida). –

+0

Mire mi solución genérica sin usar Reflection en su lugar usando árboles de expresiones. Tomó prestado algún código de su solución – Ramesh

0

Hay un CopyToDataTable extension method que hace eso por usted. Vive en System.Data.DataSetExtensions.dll

+0

Esperaba poder utilizar esto, pero obtengo un error de compilación: Error El tipo 'AnonymousType # 1' no puede usarse como tipo parámetro 'T' en el tipo genérico o método 'System.Data.DataTableExtensions.CopyToDataTable (System.Collections.Generic.IEnumerable )'. No hay conversión de referencia implícita de 'AnonymousType # 1' a 'System.Data.DataRow'. – TruMan1

+0

Sí, este no es un método útil para copiar objetos, anónimos o no, a menos que desciendan de DataRow. – dwerner

0

Prueba esto:

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

artículo original para ejemplo de código: Converting Anonymous type generated by LINQ to a DataTable type

EDIT: Pseudocódigo genérico:

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

Actualmente tengo más de 30 propiedades y quiero que esto sea genérico. ¿Hay alguna otra forma además de codificar las columnas? También podría crear una clase real en lugar de usar una clase anónima. – TruMan1

+0

Revisa mi edición. Todavía estoy pensando en cómo hacer que la segunda parte sea genérica. –

0

Puede convertir el resultado de su lista de tabla de datos mediante el siguiente función

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

Aquí es una solución genérica sin reflexionar sobre las propiedades. Tener un método de extensión de la siguiente manera

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 invocar el mismo mediante los que los parámetros serán el nombre de la columna en el orden que desee.

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

¿La variable que nunca parece cambiar? – 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; 
     } 

Después de hacer su selecto nueva que pueda para .ToList().ListToDataTable(). Esto utiliza la reflexión de ComponentModel y es (teóricamente) más rápido que System.Reflection.