Given a list of objects, I am needing to transform it into a dataset where each item in the list is represented by a row and each property is a column in the row. This DataS
I found this code on Microsoft forum. This is so far one of easiest way, easy to understand and use. This has saved me hours. I have customized this as extension method without any change to actual implementaion. Below is the code. it doesn't require much explanation.
You can use two function signature with same implementation
1) public static DataSet ToDataSetFromObject(this object dsCollection)
2) public static DataSet ToDataSetFromArrayOfObject( this object[] arrCollection). I'll be using this one in below example.
// <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;
}
To use this extension in code
Country[] objArrayCountry = null;
objArrayCountry = ....;// populate your array
if ((objArrayCountry != null)) {
dataset = objArrayCountry.ToDataSetFromArrayOfObject();
}
Apart from additionally using Reflection
to determine the properties of class Record
to take care of adding new properties, that's pretty much it.
You can do it through reflection and generics, inspecting the properties of the underlying type.
Consider this extension method that I use:
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;
}
I've written a small library myself to accomplish this task. It uses reflection only for the first time an object type is to be translated to a datatable. It emits a method that will do all the work translating an object type.
Its blazing fast. You can find it here: ModelShredder on GoogleCode
I made some changes to CMS' extension method to handle the case when the List
contains primitive or String
elements. In that case the resulting DataTable
will only have one Column
with a Row
for each of the values in the list.
At first I thought of including all value types (not only primitive types) but I didn't want Structures (which are value types) to be included.
This change arose from my need of converting a List(Of Long)
, or List<long>
, into a DataTable
to use it as a Table-Valued Parameter in a MS SQL 2008 Stored Procedure.
I'm sorry my code is in VB even though this question is tagged c#; my project is in VB (NOT my choice) and it shouldn't be hard to apply the changes in 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