Thomas
Thomas

Reputation: 34188

How to convert LINQ ToList() function to datatable c#

this is how my xml look like

<?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>

i parse the above xml using LINQ this way

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

i want to list xml data in tabular format just like sql query return data. i search and got a link http://stackoverflow.com/questions/18608959/convert-linq-query-results-to-datatable-c-sharp the link suggested to use CopyToDataTable() extension method which i followed but still i am not getting result in tabular format.

so guide me how could i convert my ToList() to Datatable as a result output would look like

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

here is my full code for CopyToDataTable which i got from MSDN

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

and then call this way DataTable dtt =document.Descendants("orders").Select(c => c).ToList().CopyToDataTable(); but it did not worked the way i want the output.

Upvotes: 0

Views: 1540

Answers (3)

J&#252;rgen Steinblock
J&#252;rgen Steinblock

Reputation: 31723

Microsoft release a sample class which works pretty reliable and fast (fast in terms of as fast as a reflection based solution can be).

It's called ObjectShredder and you can grab it from msdn

Works best with this 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);
    }

}

But since document.Descendants("orders") returns XElement types which don't have usefull properties but some XElements I would suggest you use an anonymous type instead.

Usage:

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();

Upvotes: 1

user5135401
user5135401

Reputation: 218

I can't add comment yet, so here what you can do

based on the answer in the following thread

you need to reference the 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;
        }

and if you are working with 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;
        }

UPDATED

you can use the following function

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

and to use the function

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

here working sample

Upvotes: 1

Ace McCloud
Ace McCloud

Reputation: 900

Use readxml to convert xml to data tabvle. eg Below ,values is of type IEnumerable

      IEnumerable<XElement> values =
                    from el in root.Elements("orders")
                    select el;


     DataSet ds = new DataSet();

                ds.ReadXml(new StringReader(new XElement("Root", values).ToString()));

DataTable d = ds.Tables[0];

            dgv.DataSource = d;  // dgv is a Data Grid View

Upvotes: 0

Related Questions