简体   繁体   English

将通用嵌套列表转换为数据表

[英]Convert Generic Nested List to Datatable

Continuation from "Convert generic List/Enumerable to DataTable?" 继续“将通用列表/枚举转换为数据表?”

I've been using the following code to covert a generic List<T> into a DataTable :我一直在使用以下代码将通用List<T>转换为DataTable

    public static DataTable ToDataTable<T>(this IList<T> data)
    {
        PropertyDescriptorCollection properties = 
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                 row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        return table;
    }

However, I now have a list List<foo> where foo contains a property List<bar> (where List<bar> can contain zero values).但是,我现在有一个列表List<foo> ,其中foo包含一个属性List<bar> (其中List<bar>可以包含零值)。

Question:题:

I want to convert a generic List<foo> containing another generic nested List<bar> such as :我想转换包含另一个通用嵌套List<bar>的通用List<foo> ,例如:

        List<bar> GenericNestedList = new List<bar>{ new bar("barA"), new bar("barB") };
        List<foo> GenericList = new List<foo> { new foo("fooA", GenericNestedList) };

To result in a DataTable :产生一个DataTable

| GenericProperty | GenericNestedProperty |
|-----------------|-----------------------|
|      fooA       |         barA          |
|      fooA       |         barB          |

The original code accounts for many properties within foo , bar would also have this requirement.原始代码在foo考虑了许多属性, bar也会有这个要求。

So far I've been able to retrieve the properties of bar , however I've been unable to figure out how to populate the Datatable when List<bar> is empty.到目前为止,我已经能够检索bar的属性,但是我一直无法弄清楚如何在List<bar>为空时填充Datatable I don't have a lot of experience writing generic methods, so my apologies for not being able to provide a lot of my workings.我没有很多编写泛型方法的经验,所以我很抱歉不能提供很多我的工作。

Other Examples:其他例子:

List列表

List<foo> GenericList = new List<foo> { new foo("fooB", new List<bar>()) };

Datatable数据表

| GenericProperty | GenericNestedProperty |
|-----------------|-----------------------|
|      fooB       |                       |

List列表

        List<bar> GenericNestedListA = new List<bar>{ new bar("barC"), new bar("barD") };
        List<bar> GenericNestedListB = new List<bar> { new bar("barE"), new bar("barF") };

        List<foo> GenericList = new List<foo> { new foo("fooC", GenericNestedListA),
                                                new foo("fooD", GenericNestedListB) };

Datatable数据表

| GenericProperty | GenericNestedProperty |
|-----------------|-----------------------|
|      fooC       |         barC          |
|      fooC       |         barD          |
|      fooD       |         barE          |
|      fooD       |         barF          |

Classes:课程:

foo

class foo
{
    public string GenericProperty;
    public List<bar> GenericNestedList;

    public foo(string GenericProperty, List<bar> GenericNestedList)
    {
        this.GenericProperty = GenericProperty;
        this.GenericNestedList = GenericNestedList;
    }
}

bar酒吧

class bar
{
    public string GenericNestedProperty;

    public bar(string GenericNestedProperty)
    {
        this.GenericNestedProperty = GenericNestedProperty;
    }
}

Just a quick solution:只是一个快速的解决方案:

public DataTable CreateNestedDataTable<TOuter, TInner>(IEnumerable<TOuter> list, string innerListPropertyName)
{
    PropertyInfo[] outerProperties = typeof(TOuter).GetProperties().Where(pi => pi.Name != innerListPropertyName).ToArray();
    PropertyInfo[] innerProperties = typeof(TInner).GetProperties();
    MethodInfo innerListGetter = typeof(TOuter).GetProperty(innerListPropertyName).GetMethod;

    // set up columns
    DataTable table = new DataTable();
    foreach (PropertyInfo pi in outerProperties)
        table.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType);
    foreach (PropertyInfo pi in innerProperties)
        table.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType);

    // iterate through outer items
    foreach (TOuter outerItem in list)
    {
        var innerList = innerListGetter.Invoke(outerItem, null) as IEnumerable<TInner>;
        if (innerList == null || innerList.Count() == 0)
        {
            // outer item has no inner items
            DataRow row = table.NewRow();
            foreach (PropertyInfo pi in outerProperties)
                row[pi.Name] = pi.GetValue(outerItem) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        else
        {
            // iterate through inner items
            foreach (object innerItem in innerList)
            {
                DataRow row = table.NewRow();
                foreach (PropertyInfo pi in outerProperties)
                    row[pi.Name] = pi.GetValue(outerItem) ?? DBNull.Value;
                foreach (PropertyInfo pi in innerProperties)
                    row[pi.Name] = pi.GetValue(innerItem) ?? DBNull.Value;
                table.Rows.Add(row);
            }
        }
    }

    return table;
}

One could probably expand this even further, so it could work with multiple nested lists, or automatically recognize the properties that are nested lists.人们可能会进一步扩展这一点,因此它可以处理多个嵌套列表,或者自动识别嵌套列表的属性。 But I kept it simple here.但我在这里保持简单。

It's used like this:它是这样使用的:

var table = CreateNestedDataTable<foo, bar>(GenericList, "GenericNestedList");

Tested with your examples, it produces the desired results:用你的例子测试,它产生了预期的结果:

预期结果


Above code uses PropertyInfo.GetMethod and PropertyInfo.GetValue which were introduced with .NET 4.5.上面的代码使用了 .NET 4.5 引入的PropertyInfo.GetMethodPropertyInfo.GetValue For 4.0, make the following replacements:对于 4.0,进行以下替换:

// before
typeof(TOuter).GetProperty(innerListPropertyName).GetMethod;

// after
typeof(TOuter).GetProperty(innerListPropertyName).GetGetMethod(true);


// for each row assignment
// before
row[pi.Name] = pi.GetValue(item) ?? DBNull.Value;

// after
row[pi.Name] = pi.GetValue(item, null) ?? DBNull.Value;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM