3

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.

Community
  • 1
  • 1
Ali Umair
  • 1,386
  • 1
  • 21
  • 42

2 Answers2

3

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;
}
Grax32
  • 3,986
  • 1
  • 17
  • 32
  • Thanks Bro worked like a charm :) , I knew i was close but was unable to figure it out, guess i was too much taken away by this code. – Ali Umair Apr 28 '15 at 05:15
  • I searched for a solution that implements child objects, tjhis one didi it!!!! Thanks man!!! – Franki1986 Apr 26 '16 at 10:46
  • 1
    Excellent! I wish this answer would be on the first page of Google. This would have saved me a lot of time! Finally a solution that works with referenced classes. – VDWWD Mar 22 '17 at 14:50
0

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;
  }
Franki1986
  • 1,320
  • 1
  • 15
  • 40