将List<T>转换为包括T.CustomClass属性的datatable

3

如下链接所述,我已成功将List<T>转换为DataTable,但还有更多的问题。我的T对象基本上是一个自定义类,具有属性和对另一个类的引用。现在我需要将该类的属性添加到datatable中。

这是函数

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;
}

这是我努力定制的结果,但无法检索DataTable的整个数据行,因为T没有其子级的属性。如何添加T的子属性或获取整个数据行。
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;
}

我正在开发一个自定义的网格控件,它只能在 DataTable 上工作,因此这个功能对我来说非常关键。由于有很多需要定制的网格,我需要拥有这个函数,我不能手动创建每一个 DataTable

2个回答

3
你的函数总体上走在正确的轨道上,然而从反射中返回的属性描述符是相对于它们来自的类型的,因此 propList[i].GetValue(item) 希望 item 是子类型。即 MyParent.MyChild.Description 的属性描述符希望 item 是 MyChild 类型,而不是 MyParent。
我对你的代码做了一些更改,使其保存属性描述符数组,并依次使用每个描述符检索中间属性的值。
这段代码无法处理 NULL 值。你应该修改 GetValueFromProps 以正确处理 NULL。
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;
}

谢谢兄弟,解决得很好 :) 我知道我离成功很近了,但一直无法解决它。我猜可能是这段代码让我太沉迷了。 - Ali Umair
我搜索了一个实现子对象的解决方案,这个做到了!!!谢谢你!!! - Franki1986
1
太好了!我希望这个答案能出现在谷歌的第一页。这会为我节省很多时间!终于有一个可以与引用类一起使用的解决方案了。 - VDWWD

0
我定制了这些函数,因此相同命名空间的枚举可以工作,并且子元素具有父元素的前缀。 例如,如果类Person有一个元素car,该元素具有CarModell之类的属性,则列标题将为:“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;
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接