Using Reflection to create a DataTable from a Class?
I've just learned about Generics and I'm wondering whether I can use it to dynamically build datatables from my classes.
Or I might be missing the point here. Here is my code, what I'm trying to do is create a datatable from my existing class and populate it. However I'm getting stuck in my thought process.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
namespace Generics
{
public class Dog
{
public string Breed { get; set; }
public string Name { get; set; }
public int legs { get; set; }
public bool tail { get; set; }
}
class Program
{
public static DataTable CreateDataTable(Type animaltype)
{
DataTable return_Datatable = new DataTable();
foreach (PropertyInfo info in animaltype.GetProperties())
{
return_Datatable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
}
return return_Datatable;
}
static void Main(string[] args)
{
Dog Killer = new Dog();
Killer.Breed = "Maltese Poodle";
Killer.legs = 3;
Killer.tail = false;
Killer.Name = "Killer";
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);
//How do I continue from here?
}
}
}
Now At the DataTable
point it errors.
Also, being new to reflection and Generics, how will I actually populate the data with the Killer class?
Building up on all the previous answers, here is a version that creates a DataTable from any collection:
public static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
Type type = typeof(T);
var properties = type.GetProperties();
DataTable dataTable = new DataTable();
dataTable.TableName = typeof(T).FullName;
foreach (PropertyInfo info in properties)
{
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
foreach (T entity in list)
{
object[] values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
Here is a more compact version of David's answer that is also an extension function. I've posted the code in a C# project on Github.
public static class Extensions
{
public static DataTable ToDataTable<T>(this IEnumerable<T> self)
{
var properties = typeof(T).GetProperties();
var dataTable = new DataTable();
foreach (var info in properties)
dataTable.Columns.Add(info.Name, Nullable.GetUnderlyingType(info.PropertyType)
?? info.PropertyType);
foreach (var entity in self)
dataTable.Rows.Add(properties.Select(p => p.GetValue(entity)).ToArray());
return dataTable;
}
}
I have found that this works very well in conjunction with code to write a DataTable to CSV.
my favorite homemade function. it create and populate all at same time. throw any object.
public static DataTable ObjectToData(object o)
{
DataTable dt = new DataTable("OutputData");
DataRow dr = dt.NewRow();
dt.Rows.Add(dr);
o.GetType().GetProperties().ToList().ForEach(f =>
{
try
{
f.GetValue(o, null);
dt.Columns.Add(f.Name, f.PropertyType);
dt.Rows[0][f.Name] = f.GetValue(o, null);
}
catch { }
});
return dt;
}
The error can be resolved by changing this:
dogTable = CreateDataTable(Dog);
to this:
dogTable = CreateDataTable(typeof(Dog));
But there are some caveats with what you're trying to do. First, a DataTable
can't store complex types, so if Dog
has an instance of Cat
on it, you won't be able to add that as a column. It's up to you what you want to do in that case, but keep it in mind.
Second, I would recommend that the only time you use a DataTable
is when you're building code that knows nothing about the data its consuming. There are valid use cases for this (e.g. a user-driven data mining tool). If you already have the data in the Dog
instance, just use it.
Another little tidbit, this:
DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);
can be condensed to this:
DataTable dogTable = CreateDataTable(Dog);