¿Cómo transformo un List en un conjunto de datos?
-
22-08-2019 - |
Pregunta
Dada una lista de objetos, estoy necesitando para transformarlo en un conjunto de datos, donde cada elemento de la lista es representada por una fila y cada propiedad es una columna de la fila. Este conjunto de datos a continuación, se pasa a un función Aspose.Cells con el fin de crear un documento de Excel como un informe.
Decir que tengo lo siguiente:
public class Record
{
public int ID { get; set; }
public bool Status { get; set; }
public string Message { get; set; }
}
Dada una lista de registros, ¿cómo 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 la 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 de esta manera me deja pensando que debe haber una mejor manera ya que por lo menos si se añaden nuevos inmuebles al Registro entonces no van a aparecer en el conjunto de datos ... pero, al mismo tiempo que me permite controlar el orden de cada alojamiento se añade a la fila.
¿Alguien sabe de una mejor manera de hacer esto?
Solución
Puede hacerlo a través de la reflexión y los genéricos, la inspección de las propiedades del tipo subyacente.
Tenga en cuenta que este método de extensión que yo uso:
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;
}
Otros consejos
Además de la utilización adicional Reflection
para determinar las propiedades de Record
clase para cuidar de la adición de nuevas propiedades, que es más o menos la misma.
He encontrado este código en foro de Microsoft. Esto es hasta ahora uno de manera más sencilla, fácil de entender y utilizar. Esto me ha ahorrado horas. He personalizado esto como método de extensión sin ningún cambio en implementaion real. A continuación se muestra el código. que no requiere mucha explicación.
Se pueden utilizar dos firma de la función con la misma aplicación
1) público ToDataSetFromObject conjunto de datos estática (esto objeto dsCollection)
2) de datos pública ToDataSetFromArrayOfObject estática (esto objeto [] arrCollection). Voy a usar éste por debajo de 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();
}
He escrito una pequeña biblioteca a mí mismo para realizar esta tarea. Se utiliza la reflexión solamente por primera vez un tipo de objeto debe ser traducida a una tabla de datos. Emite un método que hacer todo el trabajo la traducción de un tipo de objeto.
Su rápida ardiente. Lo puedes encontrar aquí: ModelShredder en GoogleCode
I hecho algunos cambios en el método de extensión de CMS para manejar el caso cuando el List
contiene elementos primitivos o String
. En ese caso el DataTable
resultante sólo tendrá una Column
con un Row
para cada uno de los valores en la lista.
Al principio pensé de incluir todos los tipos de valor (no sólo los tipos primitivos), pero yo no quería Estructuras (que son tipos de valor) que se incluirán.
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 MS SQL 2008 procedimiento almacenado.
Lo siento mi es el código de VB a pesar de que esta pregunta se etiqueta c # ; mi proyecto es en VB (no es mi elección) y que no debería ser difícil de 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