2009-02-07 14 views
14

Dada una lista de objetos, necesito transformarla en un conjunto de datos donde cada elemento de la lista se representa mediante una fila y cada propiedad es una columna en la fila. Este DataSet se pasará a una función Aspose.Cells para crear un documento de Excel como informe.¿Cómo transformo una Lista <T> en un DataSet?

Decir que tengo el siguiente:

public class Record 
{ 
    public int ID { get; set; } 
    public bool Status { get; set; } 
    public string Message { get; set; } 
} 

dado una lista de registros, ¿cómo puedo transformarlo en un conjunto de datos de la siguiente manera:

ID Status Message 
1 true "message" 
2 false "message2" 
3 true "message3" 
... 

Por el momento lo único que se me ocurre es el siguiente:

DataSet ds = new DataSet 
ds.Tables.Add(); 
ds.Tables[0].Add("ID", typeof(int));  
ds.Tables[0].Add("Status", typeof(bool)); 
ds.Tables[0].Add("Message", typeof(string)); 

foreach(Record record in records) 
{ 
    ds.Tables[0].Rows.Add(record.ID, record.Status, record.Message); 
} 

Pero esto me hace pensar que debe haber una mejor manera ya que en el al menos si se agregan nuevas propiedades a Record, entonces no aparecerán en el DataSet ... pero al mismo tiempo me permite controlar el orden en que cada propiedad se agrega a la fila.

¿Alguien sabe de una mejor manera de hacer esto?

Respuesta

27

Puede hacerlo mediante reflexión y genéricos, inspeccionando las propiedades del tipo subyacente.

consideran este método de extensión que utilizo:

public static DataTable ToDataTable<T>(this IEnumerable<T> collection) 
    { 
     DataTable dt = new DataTable("DataTable"); 
     Type t = typeof(T); 
     PropertyInfo[] pia = t.GetProperties(); 

     //Inspect the properties and create the columns in the DataTable 
     foreach (PropertyInfo pi in pia) 
     { 
      Type ColumnType = pi.PropertyType; 
      if ((ColumnType.IsGenericType)) 
      { 
       ColumnType = ColumnType.GetGenericArguments()[0]; 
      } 
      dt.Columns.Add(pi.Name, ColumnType); 
     } 

     //Populate the data table 
     foreach (T item in collection) 
     { 
      DataRow dr = dt.NewRow(); 
      dr.BeginEdit(); 
      foreach (PropertyInfo pi in pia) 
      { 
       if (pi.GetValue(item, null) != null) 
       { 
        dr[pi.Name] = pi.GetValue(item, null); 
       } 
      } 
      dr.EndEdit(); 
      dt.Rows.Add(dr); 
     } 
     return dt; 
    } 
+0

Bueno, la comunidad ha hablado, así que le votaré la respuesta, aunque no podré usarla para mis propósitos, ya que quiero controlar el orden de los parámetros. Pero definitivamente voy a tener esta solución en mente ... – mezoid

+2

Oye, acabo de probar su extensión y he descubierto que si desea controlar el orden en que aparecen las columnas en la tabla de datos, debe declararlas en el orden en que los quiere en el objeto de tipo T que pasa a la extensión. ¡Eso es genial! – mezoid

1

Además de usar adicionalmente Reflection para determinar las propiedades de la clase Record para tener cuidado de agregar nuevas propiedades, eso es más o menos.

+0

Probablemente tienes razón ... aunque no es lo que quería oír. Creo que o bien necesito mejorar mi conocimiento de DataSets o alguien en Microsoft necesita pensar en una mejor manera. – mezoid

0

He escrito una pequeña biblioteca a mí mismo para realizar esta tarea. Utiliza la reflexión solo la primera vez que se traduce un tipo de objeto a una tabla de datos. Emite un método que hará todo el trabajo traduciendo un tipo de objeto.

Es increíblemente rápido. Lo puedes encontrar aquí: ModelShredder on GoogleCode

0

he hecho algunos cambios en el método de extensión de CMS para manejar el caso cuando el List contiene primitivo o String elementos. En ese caso, el DataTable resultante solo tendrá un Column con un para cada uno de los valores en la lista.

Al principio pensé en incluir todos los tipos de valores (no solo los tipos primitivos) pero no quería que se incluyeran Estructuras (que son tipos de valores).

Este cambio surgió de mi necesidad de convertir un List(Of Long), o List<long>, en un DataTable utilizarlo como un parámetro con valores de tabla en un procedimiento de MS SQL 2008 almacenado.

Lo siento, mi código está en VB aunque esta pregunta se etiquete ; mi proyecto está en VB (NO es mi elección) y no debería ser difícil aplicar los cambios en C#.

Imports System.Runtime.CompilerServices 
Imports System.Reflection 

Module Extensions 

    <Extension()> 
    Public Function ToDataTable(Of T)(ByVal collection As IEnumerable(Of T)) As DataTable 
     Dim dt As DataTable = New DataTable("DataTable") 
     Dim type As Type = GetType(T) 
     Dim pia() As PropertyInfo = type.GetProperties() 

     ' For a collection of primitive types create a 1 column DataTable 
     If type.IsPrimitive OrElse type.Equals(GetType(String)) Then 
      dt.Columns.Add("Column", type) 
     Else 
      ' Inspect the properties and create the column in the DataTable 
      For Each pi As PropertyInfo In pia 
       Dim ColumnType As Type = pi.PropertyType 
       If ColumnType.IsGenericType Then 
        ColumnType = ColumnType.GetGenericArguments()(0) 
       End If 
       dt.Columns.Add(pi.Name, ColumnType) 
      Next 

     End If 

     ' Populate the data table 
     For Each item As T In collection 
      Dim dr As DataRow = dt.NewRow() 
      dr.BeginEdit() 
      ' Set item as the value for the lone column on each row 
      If type.IsPrimitive OrElse type.Equals(GetType(String)) Then 
       dr("Column") = item 
      Else 
       For Each pi As PropertyInfo In pia 
        If pi.GetValue(item, Nothing) <> Nothing Then 
         dr(pi.Name) = pi.GetValue(item, Nothing) 
        End If 
       Next 
      End If 
      dr.EndEdit() 
      dt.Rows.Add(dr) 
     Next 
     Return dt 
    End Function 

End Module 
0

Encontré este código en el foro de Microsoft. Hasta ahora, esta es una de las formas más sencillas, fáciles de entender y usar. Esto me ha ahorrado horas. He personalizado esto como método de extensión sin ningún cambio en la implementación real. A continuación está el código. no requiere mucha explicación.

Se pueden utilizar dos firma de la función con la misma aplicación

1) ToDataSetFromObject conjunto de datos estáticos públicos (esto objeto dsCollection)

2) ToDataSetFromArrayOfObject conjunto de datos estáticos públicos (esto objeto [] arrCollection). Voy a usar este en el siguiente ejemplo.

// <summary> 
// Serialize Object to XML and then read it into a DataSet: 
// </summary> 
// <param name="arrCollection">Array of object</param> 
// <returns>dataset</returns> 

public static DataSet ToDataSetFromArrayOfObject(this object[] arrCollection) 
{ 
    DataSet ds = new DataSet(); 
    try { 
     XmlSerializer serializer = new XmlSerializer(arrCollection.GetType); 
     System.IO.StringWriter sw = new System.IO.StringWriter(); 
     serializer.Serialize(sw, dsCollection); 
     System.IO.StringReader reader = new System.IO.StringReader(sw.ToString()); 
     ds.ReadXml(reader); 
    } catch (Exception ex) { 
     throw (new Exception("Error While Converting Array of Object to Dataset.")); 
    } 
    return ds; 
} 

Para utilizar esta extensión en el código

Country[] objArrayCountry = null; 
objArrayCountry = ....;// populate your array 
if ((objArrayCountry != null)) { 
    dataset = objArrayCountry.ToDataSetFromArrayOfObject(); 
} 
Cuestiones relacionadas