如何将LINQ ToList()函数转换为datatable c#

时间:2015-07-20 14:45:12

标签: c# xml linq datatable

这就是我的xml看起来像

的样子
<?xml version="1.0" encoding="utf-8"?>
<Root>
  <orders>
    <OrderID>10248</OrderID>
    <CustomerID>VINET</CustomerID>
    <EmployeeID>5</EmployeeID>
    <OrderDate>1996-07-04T00:00:00</OrderDate>
    <RequiredDate>1996-08-01T00:00:00</RequiredDate>
    <ShippedDate>1996-07-16T00:00:00</ShippedDate>
    <ShipVia>3</ShipVia>
    <Freight>32.3800</Freight>
    <ShipName>Vins et alcools Chevalier</ShipName>
    <ShipAddress>59 rue de l'Abbaye</ShipAddress>
    <ShipCity>Reims</ShipCity>
    <ShipPostalCode>51100</ShipPostalCode>
    <ShipCountry>France</ShipCountry>
  </orders>
</Root>

我用LINQ这样解析上面的xml

document.Descendants("orders").Select(c => c).ToList()

我想以表格格式列出xml数据,就像sql查询返回数据一样。 我搜索并获得了一个链接http://stackoverflow.com/questions/18608959/convert-linq-query-results-to-datatable-c-sharp建议使用CopyToDataTable() extension方法的链接,但我仍未获得表格格式的结果。

所以请指导我如何转换我的ToList() to Datatable,结果输出看起来像

OrderID      CustomerID      EmployeeID
-------      ----------      -----------
10248        VINET           5
10249        AJAY            11
11027        Smith           09

这是我从MSDN获得的CopyToDataTable的完整代码

public static class DataSetLinqOperators
{
    public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
    {
        return new ObjectShredder<T>().Shred(source, null, null);
    }

    public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,
                                                DataTable table, LoadOption? options)
    {
        return new ObjectShredder<T>().Shred(source, table, options);
    }

}

public class ObjectShredder<T>
{
    private FieldInfo[] _fi;
    private PropertyInfo[] _pi;
    private Dictionary<string, int> _ordinalMap;
    private Type _type;

    public ObjectShredder()
    {
        _type = typeof(T);
        _fi = _type.GetFields();
        _pi = _type.GetProperties();
        _ordinalMap = new Dictionary<string, int>();
    }

    public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options)
    {
        if (typeof(T).IsPrimitive)
        {
            return ShredPrimitive(source, table, options);
        }


        if (table == null)
        {
            table = new DataTable(typeof(T).Name);
        }

        // now see if need to extend datatable base on the type T + build ordinal map
        table = ExtendTable(table, typeof(T));

        table.BeginLoadData();
        using (IEnumerator<T> e = source.GetEnumerator())
        {
            while (e.MoveNext())
            {
                if (options != null)
                {
                    table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options);
                }
                else
                {
                    table.LoadDataRow(ShredObject(table, e.Current), true);
                }
            }
        }
        table.EndLoadData();
        return table;
    }

    public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options)
    {
        if (table == null)
        {
            table = new DataTable(typeof(T).Name);
        }

        if (!table.Columns.Contains("Value"))
        {
            table.Columns.Add("Value", typeof(T));
        }

        table.BeginLoadData();
        using (IEnumerator<T> e = source.GetEnumerator())
        {
            Object[] values = new object[table.Columns.Count];
            while (e.MoveNext())
            {
                values[table.Columns["Value"].Ordinal] = e.Current;

                if (options != null)
                {
                    table.LoadDataRow(values, (LoadOption)options);
                }
                else
                {
                    table.LoadDataRow(values, true);
                }
            }
        }
        table.EndLoadData();
        return table;
    }

    public DataTable ExtendTable(DataTable table, Type type)
    {
        // value is type derived from T, may need to extend table.
        foreach (FieldInfo f in type.GetFields())
        {
            if (!_ordinalMap.ContainsKey(f.Name))
            {
                DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
                    : table.Columns.Add(f.Name, f.FieldType);
                _ordinalMap.Add(f.Name, dc.Ordinal);
            }
        }
        foreach (PropertyInfo p in type.GetProperties())
        {
            if (!_ordinalMap.ContainsKey(p.Name))
            {
                DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
                    : table.Columns.Add(p.Name, p.PropertyType);
                _ordinalMap.Add(p.Name, dc.Ordinal);
            }
        }
        return table;
    }

    public object[] ShredObject(DataTable table, T instance)
    {

        FieldInfo[] fi = _fi;
        PropertyInfo[] pi = _pi;

        if (instance.GetType() != typeof(T))
        {
            ExtendTable(table, instance.GetType());
            fi = instance.GetType().GetFields();
            pi = instance.GetType().GetProperties();
        }

        Object[] values = new object[table.Columns.Count];
        foreach (FieldInfo f in fi)
        {
            values[_ordinalMap[f.Name]] = f.GetValue(instance);
        }

        foreach (PropertyInfo p in pi)
        {
            values[_ordinalMap[p.Name]] = p.GetValue(instance, null);
        }
        return values;
    }
}

然后以这种方式调用DataTable dtt =document.Descendants("orders").Select(c => c).ToList().CopyToDataTable();,但它没有按照我想要的方式工作。

3 个答案:

答案 0 :(得分:1)

Microsoft发布了一个非常可靠且快速的示例类(快速,基于反射的解决方案可以)。

它被称为ObjectShredder,您可以从msdn

获取它

最适合使用此ExtensionMethod

public static class CustomLINQtoDataSetMethods
{
    public static DataTable CopyToDataTable<T>(this IEnumerable<T> source)
    {
        return new ObjectShredder<T>().Shred(source, null, null);
    }

    public static DataTable CopyToDataTable<T>(
        this IEnumerable<T> source, DataTable table, LoadOption? options)
    {
        return new ObjectShredder<T>().Shred(source, table, options);
    }

}

但是,由于document.Descendants("orders")返回的XElement类型不具备有用的属性,但有些XElements我会建议您使用匿名类型。

用法:

var query = from o in document.Descendants("orders")
            select new 
            { 
                OrderID = (int)o.Element("OrderID"),
                CustomerID = (string)o.Element("CustomerID"),
                EmployeeID = (int)o.Element("EmployeeID"),
            };

var table = query.ToDataTable();

答案 1 :(得分:1)

我还无法添加评论,所以在这里你可以做什么

基于以下thread

中的答案

您需要引用FastMember

public static DataTable ToDataTable<T>(this IEnumerable<T> data)
        {
            if (data == null)
                throw new ArgumentNullException("data");
            var table = new DataTable("sd");
            using (var reader = ObjectReader.Create(data))
            {
                table.Load(reader);
            }
            return table;
        }

如果您正在使用DataContext(linq-to-sql)

        public static DataTable ToDataTable<T>(this IQueryable<T> query, DataContext context)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            IDbCommand cmd = context.GetCommand(query.AsQueryable());
            SqlDataAdapter adapter = new SqlDataAdapter();
            adapter.SelectCommand = (SqlCommand)cmd;
            DataTable dt = new DataTable("sd");
            try
            {
                cmd.Connection.Open();
                adapter.FillSchema(dt, SchemaType.Source);
                adapter.Fill(dt);
            }
            finally
            {
                cmd.Connection.Close();
            }
            return dt;
        }

<强>已更新

您可以使用以下功能

public static DataTable ConvertToDataTable(IEnumerable<XElement> data)
    {       
        var table = new DataTable();
        // create the columns
        foreach(var xe in data.First().Descendants())
            table.Columns.Add(xe.Name.LocalName,typeof(string));
        // fill the data
        foreach(var item in data)
        {
            var row = table.NewRow();
            foreach(var xe in item.Descendants())
                row[xe.Name.LocalName] = xe.Value;
            table.Rows.Add(row);
        }
        return table;
    }

并使用该功能

var table = ConvertToDataTable(document.Descendants("orders"));

此处正在工作sample

答案 2 :(得分:0)

使用readxml将xml转换为数据选项卡。例如,下面的值是IEnumerable类型

com.android.support:appcompat-v7:22.2.0