Reputation: 1424
As following this i have successfully converted List<T>
to DataTable
but there is something more. My T object
is basically a custom class having properties and also refernece to another class. Now i need to add that class's properties to the datatable also.
This is the function
public static DataTable ToDataTable<T>(this IList<T> list)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0; i < properties.Count; i++)
{
PropertyDescriptor prop = properties[i];
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
object[] values = new object[properties.Count];
foreach (T item in list)
{
for (int i = 0; i < values.Length; i++)
values[i] = properties[i].GetValue(item) ?? DBNull.Value;
table.Rows.Add(values);
}
return table;
}
This is my effort to customize it but unable to retrieve an entire row of data for the DataTable
. As T is not having the properties of it's child. How can i add child's properties of T
or get an entire row of data.
private static DataTable AddColumnsForProperties(DataTable dt, PropertyDescriptor p, ref List<PropertyDescriptor> properties)
{
if (p.PropertyType.Namespace.ToLower().StartsWith("mynamespace"))
{
var allProperties = p.GetChildProperties();
foreach (PropertyDescriptor item in allProperties)
{
if (item.PropertyType.Namespace.ToLower().StartsWith("mynamespace"))
AddColumnsForProperties(dt, item, ref properties);
else
if (!dt.Columns.Contains(item.Name))
{
dt.Columns.Add(item.Name, Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType);
properties.Add(item);
}
}
}
else
if (!dt.Columns.Contains(p.Name))
{
dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
properties.Add(p);
}
return dt;
}
public static DataTable ToDataTable<T>(this IList<T> list)
{
DataTable table = null;
if (list != null && list.Count > 0)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
List<PropertyDescriptor> propList = new List<PropertyDescriptor>();
table = new DataTable();
foreach (PropertyDescriptor item in properties)
{
AddColumnsForProperties(table, item, ref propList);
}
object[] values = new object[propList.Count];
foreach (T item in list)
{
for (int i = 0; i < values.Length; i++)
values[i] = propList[i].GetValue(item) ?? DBNull.Value;
table.Rows.Add(values);
}
}
return table;
}
I am working on a custom grid control that only works on DataTables so this functionality is crucial to me.As there are a lots of grids that need customization i need to have this function I cannot create every DataTable
manually.
Upvotes: 3
Views: 1368
Reputation: 1436
I customized this functions, so e.g. enums of the same namespace work, and so that child elements has the prefix of the parent element. E.g. if a class Person has an element car that has properties like CarModell the column caption would be: 'Person.Car.CarModell'.
private static void AddColumnsForProperties(string myNamespace, string parentName, DataTable dt, List<PropertyDescriptor> p, ref List<PropertyDescriptor[]> properties)
{
var pLast = p.Last();
if (pLast.PropertyType.Namespace != null && pLast.PropertyType.Namespace.StartsWith(myNamespace))
{
var allProperties = pLast.GetChildProperties();
if (allProperties.Count > 0)
{
foreach (PropertyDescriptor item in allProperties)
{
var newP = p.ToList();
newP.Add(item);
string childParentName = !String.IsNullOrEmpty(parentName)
? String.Format("{0}.{1}.{2}", parentName, pLast.Name, item.Name)
: String.Format("{0}.{1}", pLast.Name, item.Name);
if (item.PropertyType.Namespace != null && item.PropertyType.Namespace.ToLower().StartsWith(myNamespace))
{
AddColumnsForProperties(myNamespace, childParentName, dt, newP, ref properties);
}
else if (!dt.Columns.Contains(childParentName))
{
dt.Columns.Add(childParentName, Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType);
properties.Add(newP.ToArray());
}
}
}
else if (!dt.Columns.Contains(pLast.Name))
{
dt.Columns.Add(pLast.Name, Nullable.GetUnderlyingType(pLast.PropertyType) ?? pLast.PropertyType);
properties.Add(p.ToArray());
}
}
else if (!dt.Columns.Contains(pLast.Name))
{
dt.Columns.Add(pLast.Name, Nullable.GetUnderlyingType(pLast.PropertyType) ?? pLast.PropertyType);
properties.Add(p.ToArray());
}
}
public static DataTable ToDataTable<T>(this IList<T> list)
{
DataTable table = null;
if (list != null && list.Count > 0)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
List<PropertyDescriptor[]> propList = new List<PropertyDescriptor[]>();
table = new DataTable();
foreach (PropertyDescriptor item in properties)
{
// Arrays oder Collections werden nicht in die DataTable eingefügt, da dies eher eine Art Master-Detail-Table ist.
if (IsArrayOrCollection(item.PropertyType))
{
continue;
}
AddColumnsForProperties(typeof(T).Namespace, null, table, (new[] { item }).ToList(), ref propList);
}
object[] values = new object[propList.Count];
foreach (T item in list)
{
for (int i = 0; i < values.Length; i++)
values[i] = GetValueFromProps(propList[i], item) ?? DBNull.Value;
table.Rows.Add(values);
}
}
return table;
}
Upvotes: 0
Reputation: 4059
Your function is generally on the right track, however, the property descriptors returned from reflection are relative to the type they came from, so propList[i].GetValue(item)
expects item to be the child type. i.e the property descriptor for MyParent.MyChild.Description expects item to be of type MyChild, not MyParent.
I made a few changes to your code so that it holds an array of property descriptors and uses each of them in turn to retrieve the value of the intermediate properties.
This code does not handle NULL values. You should probably alter GetValueFromProps to handle NULLs properly.
private static DataTable AddColumnsForProperties(string myNamespace, DataTable dt, List<PropertyDescriptor> p, ref List<PropertyDescriptor[]> properties)
{
var pLast = p.Last();
if (pLast.PropertyType.Namespace.StartsWith(myNamespace))
{
var allProperties = pLast.GetChildProperties();
foreach (PropertyDescriptor item in allProperties)
{
var newP = p.ToList();
newP.Add(item);
if (item.PropertyType.Namespace.ToLower().StartsWith(myNamespace))
AddColumnsForProperties(myNamespace, dt, newP, ref properties);
else
if (!dt.Columns.Contains(item.Name))
{
dt.Columns.Add(item.Name, Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType);
properties.Add(newP.ToArray());
}
}
}
else
if (!dt.Columns.Contains(pLast.Name))
{
dt.Columns.Add(pLast.Name, Nullable.GetUnderlyingType(pLast.PropertyType) ?? pLast.PropertyType);
properties.Add(p.ToArray());
}
return dt;
}
static object GetValueFromProps(PropertyDescriptor[] descriptors, object item)
{
var result = item;
foreach (var descriptor in descriptors)
{
result = descriptor.GetValue(result);
}
return result;
}
public static DataTable ToDataTable<T>(this IList<T> list)
{
DataTable table = null;
if (list != null && list.Count > 0)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
List<PropertyDescriptor[]> propList = new List<PropertyDescriptor[]>();
table = new DataTable();
foreach (PropertyDescriptor item in properties)
{
AddColumnsForProperties(typeof(T).Namespace, table, (new[] { item }).ToList(), ref propList);
}
object[] values = new object[propList.Count];
foreach (T item in list)
{
for (int i = 0; i < values.Length; i++)
values[i] = GetValueFromProps(propList[i], item) ?? DBNull.Value;
table.Rows.Add(values);
}
}
return table;
}
Upvotes: 3