310

I have few methods that returns different Generic Lists.

Exists in .net any class static method or whatever to convert any list into a datatable? The only thing that i can imagine is use Reflection to do this.

IF i have this:

List<Whatever> whatever = new List<Whatever>();

(This next code doesn't work of course, but i would like to have the possibility of:

DataTable dt = (DataTable) whatever;
Simon Martin
  • 4,203
  • 7
  • 56
  • 93
Josema
  • 4,638
  • 6
  • 22
  • 15
  • 2
    Of course, a good question would be "why?" - when List is in many cases a better tool than DataTable ;-p Each to their own, I guess... – Marc Gravell Feb 19 '09 at 08:30
  • 1
    I think this one may be a duplicate of this question: http://stackoverflow.com/questions/523153/how-do-i-transform-a-listt-into-a-dataset It even has a near identical answer. :-) – mezoid Feb 19 '09 at 08:30
  • 2
    @MarcGravell: My "why?" is List manipulation (Traversing columns & rows). I'm trying to make a pivot from a List and accessing the properties via reflexion it's a pain. I'm doing it wrong? – Eduardo Molteni Sep 21 '12 at 16:22
  • 1
    @Eduardo there are any number of tools to remove the reflection pain there - FastMember leaps to mind. It *could* also be that a DataTable is useful to specific scenarios - it all depends on the context. Perhaps the biggest problem is people using DataTable for all data storage *just because it exists*, without taking the time to consider the options and their scenario. – Marc Gravell Sep 21 '12 at 16:32
  • @EduardoMolteni if you are interested, I updated FastMember to have direct support for this - see the updated answer – Marc Gravell Jun 07 '13 at 09:32
  • If you just what to pivot why not use the linqLib http://linqlib.codeplex.com/ it implements almost anything you can think of for IEnumerable. – Pedro.The.Kid Jan 31 '14 at 12:32
  • Cross post to this answer that helped me. https://stackoverflow.com/questions/3839022/listt-to-dataview – Jeff Blumenthal Aug 23 '18 at 13:16
  • I have a need to transfer a List from a EnumerateDirectories to a datatable so I can use a bulkcopy into a database. As things stand, my "foreach( item in list)" to build a datatable is very slow. I have the same question as the OP... – Kurt Hill Jan 06 '22 at 07:09

28 Answers28

372

Here's a nice 2013 update using FastMember from NuGet:

IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
    table.Load(reader);
}

This uses FastMember's meta-programming API for maximum performance. If you want to restrict it to particular members (or enforce the order), then you can do that too:

IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) {
    table.Load(reader);
}

Editor's Dis/claimer: FastMember is a Marc Gravell project. It's gold and full-on flies!


Yes, this is pretty much the exact opposite of this one; reflection would suffice - or if you need quicker, HyperDescriptor in 2.0, or maybe Expression in 3.5. Actually, HyperDescriptor should be more than adequate.

For example:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    for(int i = 0 ; i < props.Count ; i++)
    {
        PropertyDescriptor prop = props[i];
        table.Columns.Add(prop.Name, prop.PropertyType);
    }
    object[] values = new object[props.Count];
    foreach (T item in data)
    {
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = props[i].GetValue(item);
        }
        table.Rows.Add(values);
    }
    return table;        
}

Now with one line you can make this many many times faster than reflection (by enabling HyperDescriptor for the object-type T).


Edit re performance query; here's a test rig with results:

Vanilla 27179
Hyper   6997

I suspect that the bottleneck has shifted from member-access to DataTable performance... I doubt you'll improve much on that...

Code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
public class MyData
{
    public int A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public decimal D { get; set; }
    public string E { get; set; }
    public int F { get; set; }
}

static class Program
{
    static void RunTest(List<MyData> data, string caption)
    {
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < 500; i++)
        {
            data.ToDataTable();
        }
        watch.Stop();
        Console.WriteLine(caption + "\t" + watch.ElapsedMilliseconds);
    }
    static void Main()
    {
        List<MyData> foos = new List<MyData>();
        for (int i = 0 ; i < 5000 ; i++ ){
            foos.Add(new MyData
            { // just gibberish...
                A = i,
                B = i.ToString(),
                C = DateTime.Now.AddSeconds(i),
                D = i,
                E = "hello",
                F = i * 2
            });
        }
        RunTest(foos, "Vanilla");
        Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(
            typeof(MyData));
        RunTest(foos, "Hyper");
        Console.ReadLine(); // return to exit        
    }
}
Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 4
    Well "as is", it'll be about as quick as reflection. If you enable HyperDescriptor, it will thrash reflection hands down... I'll run a quick test... (2 minutes) – Marc Gravell Feb 19 '09 at 08:36
  • Expression was mentioned for 3.5. If used how would it affect the code, is there any sample ? – MicMit Mar 30 '10 at 06:40
  • @MicMit - it would make it more complex ;-p In all seriousness, I *could* put an example up but it would take quite a bit of effort - would it still be of interest? – Marc Gravell Apr 30 '10 at 06:18
  • I use this to debug Lists with the Visual Studio nice "grid viewer" that works for datatables. – joeriks May 30 '12 at 07:13
  • 3
    @MarcGravell Yes I would be very interested in the Expression solution. For needing something fast + learning effect. Thank you Marc! – Elisabeth Sep 21 '12 at 09:25
  • How do you make this a non-static method? I can't seem to adapt this to work with a normal "non static" class without getting compilation errors. – NickG May 30 '13 at 09:27
  • @Nick you... remove the `static` modifier; note that a non-static class can still have `static` methods; it is the other way around that is blocked: a `static` class cannot have instance methods – Marc Gravell May 30 '13 at 09:41
  • hi marc, `FastMember` is used to map `IEnumerable` to `DataTable` right? Do you have something in reverse? `DataTable` to `IEnumerable`? – John Woo Oct 02 '13 at 14:23
  • I found this extention fail for input: `List(){"a","b","c"}` – Ankush Madankar Feb 04 '14 at 10:43
  • 2
    It's probably worth mentioning that you are the author of FastMember for transparency. Your edit comes off as if it's just this great package that's now available that you happened to have come across. – Ellesedil Jun 10 '14 at 18:43
  • 19
    @Ellesedil I try hard to remember to explicitly disclose such things, but since I am not *selling* anything (but rather am making many hours of work freely available) I confess I don't feel huge amounts of *guilt* here... – Marc Gravell Jun 10 '14 at 19:25
  • This will fail if `T` has a `public` property that is `WriteOnly`(which has a `set` but not `get`) – Aniket Inge Aug 25 '14 at 10:00
  • Another thing,(I might be wrong about FastMember) but using Reflection(as in your code) allows you to add more goodies, such as, friendlier name for each data column, formatting, validations declaratively etc. – Aniket Inge Aug 25 '14 at 10:06
  • 3
    your method ToDataTable doesnt support nullable fields: Additional information: DataSet does not support System.Nullable<>. – Dainius Kreivys Oct 27 '15 at 15:16
285

I had to modify Marc Gravell's sample code to handle nullable types and null values. I have included a working version below. Thanks Marc.

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;
}
Marc.2377
  • 7,807
  • 7
  • 51
  • 95
Mary Hamlin
  • 4,137
  • 2
  • 26
  • 19
  • This is an excellent answer. I would love to see this example expanded out to handle a group by list that would contain an item property and have columns created in the same way above. – Unknown Coder Jan 24 '14 at 22:22
  • 3
    To achieve that @Jim Beam, change the method signature to accept the return of GroupBy: `public static DataTable ToDataTable(this IEnumerable> data)` Then, add an extra column before the foreach loop: `table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey));` And then add a loop around the data loop where you iterate the groups: foreach (IGrouping group in data) { foreach (T item in group.Items) { See this GIST for full details: https://gist.github.com/rickdailey/8679306 – Rick Dailey Jan 29 '14 at 00:22
  • hey, is there a way to handle an object with inner objects? I just want the inner Properties to appear as columns after the parent object's columns – heyNow Sep 03 '14 at 20:43
  • @heyNow, I am sure that there is. But I didn't really have a need for that functionality with what I was doing and so left it for someone else to extend. :) – Mary Hamlin Sep 04 '14 at 13:39
  • 2
    This is an old post so not sure how useful this comment is, but there is one sneaky bug in this `ToDataTable` method. If `T` implements an interface `typeof(T)` may return the interface type rather than the actual class of the object, resulting in an empty `DataTable`. Replacing it with `data.First().GetType()` should fix it. – Janilson May 08 '18 at 15:55
  • Why is it that when I put this code in my program, I got this error? "extension method must be defined in a non-generic static class" – TPG Jan 17 '19 at 06:03
  • am using hyperproperty. compiled it to target .net 4.5.2. with that the above code is returning empty dataset. this line TypeDescriptor.GetProperties(type(T)) is returning properties object with count 0 becacuse of which table is not getting build. can anyone help? thanks. – user1447718 Mar 26 '20 at 18:23
  • for anyone who can't get this to work please take a big note that this doesn't work with fields.it only works with properties. – Persk Jul 04 '20 at 09:49
18

Another approach is the above:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
kostas ch.
  • 1,960
  • 1
  • 17
  • 30
  • Very very good...but it threw Exception of type 'System.OutOfMemoryException' . I used it with 500 000 items...But thank you for this. – st_stefanov Nov 06 '18 at 13:33
  • 2
    This is by far the cleanest solution I have found on the net. Great work! – Sarah Aug 06 '19 at 00:41
  • Be careful as the data type of DataTable is not same as List. For example: decimal in object is double in DataTable. – qnguyen Sep 10 '20 at 00:36
15

A small change to Marc's answer to make it work with value types like List<string> to data table:

public static DataTable ListToDataTable<T>(IList<T> data)
{
    DataTable table = new DataTable();

    //special handling for value types and string
    if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
    {

        DataColumn dc = new DataColumn("Value", typeof(T));
        table.Columns.Add(dc);
        foreach (T item in data)
        {
            DataRow dr = table.NewRow();
            dr[0] = item;
            table.Rows.Add(dr);
        }
    }
    else
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        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)
            {
                try
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                catch (Exception ex)
                {
                    row[prop.Name] = DBNull.Value;
                }
            }
            table.Rows.Add(row);
        }
    }
    return table;
}
Joel Christophel
  • 2,604
  • 4
  • 30
  • 49
Onur Omer
  • 506
  • 3
  • 12
  • How to make it for List ? – Muflix Sep 01 '16 at 08:11
  • 1
    Method above will work for int (and other value types) too... int is a value type. see: https://msdn.microsoft.com/en-us/library/s1ax56ch.aspx – Onur Omer Sep 01 '16 at 17:21
  • I like this because it does not depend on using an extension method. Works well for older codebases that may not have access to Extension Methods. – webworm Feb 15 '19 at 14:50
15

This is a simple mix of the solutions. It work with Nullable types.

public static DataTable ToDataTable<T>(this IList<T> list)
{
  PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
  DataTable table = new DataTable();
  for (int i = 0; i < props.Count; i++)
  {
    PropertyDescriptor prop = props[i];
    table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
  }
  object[] values = new object[props.Count];
  foreach (T item in list)
  {
    for (int i = 0; i < values.Length; i++)
      values[i] = props[i].GetValue(item) ?? DBNull.Value;
    table.Rows.Add(values);
  }
  return table;
}
A.Baudouin
  • 2,855
  • 3
  • 24
  • 28
13

This link on MSDN is worth a visit: How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a DataRow

This adds an extension method that lets you do this:

// Create a sequence. 
Item[] items = new Item[] 
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"}, 
  new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
  new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
  new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.
var query = from i in items
             where i.Price > 9.99
             orderby i.Price
             select i;

// Load the query results into new DataTable.
DataTable table = query.CopyToDataTable();
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • @PaulWilliams Thanks, I use this code for years without a problem so far. But since I didn't copy the example code from microsoft and only linked to the website the other solutions are at least more compliant with the answer best practices https://stackoverflow.com/help/how-to-answer – Jürgen Steinblock Mar 03 '20 at 15:54
9
List<YourModel> data = new List<YourModel>();
DataTable dataTable = Newtonsoft.Json.JsonConvert.DeserializeObject<DataTable>(Newtonsoft.Json.JsonConvert.SerializeObject(data));
Egemen Çiftci
  • 699
  • 5
  • 13
user12815245
  • 91
  • 1
  • 1
  • 1
    While this code may answer the question, providing additional context regarding *how* and/or *why* it solves the problem would improve the answer's long-term value. – Klaus Gütter Jul 21 '20 at 13:47
  • awesome.. I use this way to achieve this case – toha Aug 01 '22 at 14:36
7
public DataTable ConvertToDataTable<T>(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;
}
Richard
  • 381
  • 2
  • 10
dikokob
  • 85
  • 3
  • 12
6
It's also possible through XmlSerialization.
The idea is - serialize to `XML` and then `readXml` method of `DataSet`.

I use this code (from an answer in SO, forgot where)

        public static string SerializeXml<T>(T value) where T : class
    {
        if (value == null)
        {
            return null;
        }

        XmlSerializer serializer = new XmlSerializer(typeof(T));

        XmlWriterSettings settings = new XmlWriterSettings();

        settings.Encoding = new UnicodeEncoding(false, false);
        settings.Indent = false;
        settings.OmitXmlDeclaration = false;
        // no BOM in a .NET string

        using (StringWriter textWriter = new StringWriter())
        {
            using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
            {
               serializer.Serialize(xmlWriter, value);
            }
            return textWriter.ToString();
        }
    }

so then it's as simple as:

            string xmlString = Utility.SerializeXml(trans.InnerList);

        DataSet ds = new DataSet("New_DataSet");
        using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
        { 
            ds.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
            ds.ReadXml(reader); 
        }

Not sure how it stands against all the other answers to this post, but it's also a possibility.
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
Mithir
  • 2,355
  • 2
  • 25
  • 37
6

try this

public static DataTable ListToDataTable<T>(IList<T> lst)
{

    currentDT = CreateTable<T>();

    Type entType = typeof(T);

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (T item in lst)
    {
        DataRow row = currentDT.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {

            if (prop.PropertyType == typeof(Nullable<decimal>) || prop.PropertyType == typeof(Nullable<int>) || prop.PropertyType == typeof(Nullable<Int64>))
            {
                if (prop.GetValue(item) == null)
                    row[prop.Name] = 0;
                else
                    row[prop.Name] = prop.GetValue(item);
            }
            else
                row[prop.Name] = prop.GetValue(item);                    

        }
        currentDT.Rows.Add(row);
    }

    return currentDT;
}

public static DataTable CreateTable<T>()
{
    Type entType = typeof(T);
    DataTable tbl = new DataTable(DTName);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (PropertyDescriptor prop in properties)
    {
        if (prop.PropertyType == typeof(Nullable<decimal>))
             tbl.Columns.Add(prop.Name, typeof(decimal));
        else if (prop.PropertyType == typeof(Nullable<int>))
            tbl.Columns.Add(prop.Name, typeof(int));
        else if (prop.PropertyType == typeof(Nullable<Int64>))
            tbl.Columns.Add(prop.Name, typeof(Int64));
        else
             tbl.Columns.Add(prop.Name, prop.PropertyType);
    }
    return tbl;
}
Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Sadegh
  • 6,654
  • 4
  • 34
  • 44
5

I've written a small library myself to accomplish this task. It uses reflection only for the first time an object type is to be translated to a datatable. It emits a method that will do all the work translating an object type.

Its blazing fast. You can find it here:

yzorg
  • 4,224
  • 3
  • 39
  • 57
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172
5

To Convert Generic list into DataTable

using Newtonsoft.Json;

public DataTable GenericToDataTable(IList<T> list)
{
    var json = JsonConvert.SerializeObject(list);
    DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
    return dt;
}
5

Marc Gravell's answer but in VB.NET

Public Shared Function ToDataTable(Of T)(data As IList(Of T)) As DataTable
    Dim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(T))
    Dim table As New DataTable()
    For i As Integer = 0 To props.Count - 1
            Dim prop As PropertyDescriptor = props(i)
            table.Columns.Add(prop.Name, prop.PropertyType)
    Next
    Dim values As Object() = New Object(props.Count - 1) {}
    For Each item As T In data
            For i As Integer = 0 To values.Length - 1
                    values(i) = props(i).GetValue(item)
            Next
            table.Rows.Add(values)
    Next
    Return table
End Function
Craig Gjerdingen
  • 1,844
  • 1
  • 21
  • 21
4

A 2019 answer if you're using .NET Core - use the Nuget ToDataTable library. Advantages:

Disclaimer - I'm the author of ToDataTable

Performance - I span up some Benchmark .Net tests and included them in the ToDataTable repo. The results were as follows:

Creating a 100,000 Row Datatable:

                           MacOS         Windows
Reflection                 818.5 ms      818.3 ms
FastMember from           1105.5 ms      976.4 ms
 Mark's answer
Improved FastMember        524.6 ms      456.4 ms
ToDataTable                449.0 ms      376.5 ms

The FastMember method suggested in Marc's answer seemed to perform worse than Mary's answer which used reflection, but I rolled another method using a FastMember TypeAccessor and it performed much better. Nevertheless the ToDataTable package outperformed the lot.

Chris HG
  • 1,412
  • 16
  • 20
2

I also had to come up with an alternate solution, as none of the options listed here worked in my case. I was using an IEnumerable which returned an IEnumerable and the properties couldn't be enumerated. This did the trick:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ConvertToDataTable<T>(this IEnumerable<T> data)
{
    List<IDataRecord> list = data.Cast<IDataRecord>().ToList();

    PropertyDescriptorCollection props = null;
    DataTable table = new DataTable();
    if (list != null && list.Count > 0)
    {
        props = TypeDescriptor.GetProperties(list[0]);
        for (int i = 0; i < props.Count; i++)
        {
            PropertyDescriptor prop = props[i];
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }
    }
    if (props != null)
    {
        object[] values = new object[props.Count];
        foreach (T item in data)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = props[i].GetValue(item) ?? DBNull.Value;
            }
            table.Rows.Add(values);
        }
    }
    return table;
}
Michael Brown
  • 1,585
  • 1
  • 22
  • 36
2
  using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.ComponentModel;

public partial class Default3 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DataTable dt = new DataTable();
        dt = lstEmployee.ConvertToDataTable();
    }
    public static DataTable ConvertToDataTable<T>(IList<T> list) where T : class
    {
        try
        {
            DataTable table = CreateDataTable<T>();
            Type objType = typeof(T);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
            foreach (T item in list)
            {
                DataRow row = table.NewRow();
                foreach (PropertyDescriptor property in properties)
                {
                    if (!CanUseType(property.PropertyType)) continue;
                    row[property.Name] = property.GetValue(item) ?? DBNull.Value;
                }

                table.Rows.Add(row);
            }
            return table;
        }
        catch (DataException ex)
        {
            return null;
        }
        catch (Exception ex)
        {
            return null;
        }

    }
    private static DataTable CreateDataTable<T>() where T : class
    {
        Type objType = typeof(T);
        DataTable table = new DataTable(objType.Name);
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
        foreach (PropertyDescriptor property in properties)
        {
            Type propertyType = property.PropertyType;
            if (!CanUseType(propertyType)) continue;

            //nullables must use underlying types
            if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                propertyType = Nullable.GetUnderlyingType(propertyType);
            //enums also need special treatment
            if (propertyType.IsEnum)
                propertyType = Enum.GetUnderlyingType(propertyType);
            table.Columns.Add(property.Name, propertyType);
        }
        return table;
    }


    private static bool CanUseType(Type propertyType)
    {
        //only strings and value types
        if (propertyType.IsArray) return false;
        if (!propertyType.IsValueType && propertyType != typeof(string)) return false;
        return true;
    }
}
2

I realize that this has been closed for a while; however, I had a solution to this specific problem but needed a slight twist: the columns and data table needed to be predefined / already instantiated. Then I needed to simply insert the types into the data table.

So here's an example of what I did:

public static class Test
{
    public static void Main()
    {
        var dataTable = new System.Data.DataTable(Guid.NewGuid().ToString());

        var columnCode = new DataColumn("Code");
        var columnLength = new DataColumn("Length");
        var columnProduct = new DataColumn("Product");

        dataTable.Columns.AddRange(new DataColumn[]
            {
                columnCode,
                columnLength,
                columnProduct
            });

        var item = new List<SomeClass>();

        item.Select(data => new
        {
            data.Id,
            data.Name,
            data.SomeValue
        }).AddToDataTable(dataTable);
    }
}

static class Extensions
{
    public static void AddToDataTable<T>(this IEnumerable<T> enumerable, System.Data.DataTable table)
    {
        if (enumerable.FirstOrDefault() == null)
        {
            table.Rows.Add(new[] {string.Empty});
            return;
        }

        var properties = enumerable.FirstOrDefault().GetType().GetProperties();

        foreach (var item in enumerable)
        {
            var row = table.NewRow();
            foreach (var property in properties)
            {
                row[property.Name] = item.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, item, null);
            }
            table.Rows.Add(row);
        }
    }
}
brenton
  • 558
  • 3
  • 18
  • can you show me with example. how I use extension method for addtodataTable() methods – Abhishek B. Dec 09 '15 at 18:25
  • This code has an example in it already - take a look at the Main() method. The last bit of code has the extension being used. – brenton Dec 09 '15 at 21:12
  • For further reading, please look at this article from MSDN about extension methods: https://msdn.microsoft.com/en-us/library/bb383977.aspx – brenton Dec 09 '15 at 21:13
2
  private DataTable CreateDataTable(IList<T> item)
        {
            Type type = typeof(T);
            var properties = type.GetProperties();

            DataTable dataTable = new DataTable();
            foreach (PropertyInfo info in properties)
            {
                dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
            }

            foreach (T entity in item)
            {
                object[] values = new object[properties.Length];
                for (int i = 0; i < properties.Length; i++)
                {
                    values[i] = properties[i].GetValue(entity);
                }

                dataTable.Rows.Add(values);
            }
            return dataTable;
        }
1

If you are using VB.NET then this class does the job.

Imports System.Reflection
''' <summary>
''' Convert any List(Of T) to a DataTable with correct column types and converts Nullable Type values to DBNull
''' </summary>

Public Class ConvertListToDataset

    Public Function ListToDataset(Of T)(ByVal list As IList(Of T)) As DataTable

        Dim dt As New DataTable()
        '/* Create the DataTable columns */
        For Each pi As PropertyInfo In GetType(T).GetProperties()
            If pi.PropertyType.IsValueType Then
                Debug.Print(pi.Name)
            End If
            If IsNothing(Nullable.GetUnderlyingType(pi.PropertyType)) Then
                dt.Columns.Add(pi.Name, pi.PropertyType)
            Else
                dt.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType))
            End If
        Next

        '/* Populate the DataTable with the values in the Items in List */
        For Each item As T In list
            Dim dr As DataRow = dt.NewRow()
            For Each pi As PropertyInfo In GetType(T).GetProperties()
                dr(pi.Name) = IIf(IsNothing(pi.GetValue(item)), DBNull.Value, pi.GetValue(item))
            Next
            dt.Rows.Add(dr)
        Next
        Return dt

    End Function

End Class
1

if you have properties in your class this line of code is OK !!

PropertyDescriptorCollection props =
            TypeDescriptor.GetProperties(typeof(T));

but if you have all public fields then use this:

public static DataTable ToDataTable<T>(  IList<T> data)
        {
        FieldInfo[] myFieldInfo;
        Type myType = typeof(T);
        // Get the type and fields of FieldInfoClass.
        myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
            | BindingFlags.Public);

        DataTable dt = new DataTable();
        for (int i = 0; i < myFieldInfo.Length; i++)
            {
            FieldInfo property = myFieldInfo[i];
            dt.Columns.Add(property.Name, property.FieldType);
            }
        object[] values = new object[myFieldInfo.Length];
        foreach (T item in data)
            {
            for (int i = 0; i < values.Length; i++)
                {
                values[i] = myFieldInfo[i].GetValue(item);
                }
            dt.Rows.Add(values);
            }
        return dt;
        }

the original answer is from above , I just edited to use fields instead of properties

and to use it do this

 DataTable dt = new DataTable();
            dt = ToDataTable(myBriefs);
            gridData.DataSource = dt;
            gridData.DataBind();
masoud Cheragee
  • 350
  • 3
  • 9
1

To convert a generic list to data table, you could use the DataTableGenerator

This library lets you convert your list into a data table with multi-feature like

  • Translate data table header
  • specify some column to show
Matt Qafouri
  • 1,449
  • 2
  • 12
  • 26
1

You can try something like below

public static DataTable GetDataTableFromObjects(object[] objects)
{
    if (objects != null && objects.Length > 0)
    {
        Type t = objects[0].GetType();
        DataTable dt = new DataTable(t.Name);
        foreach (PropertyInfo pi in t.GetProperties())
        {
            dt.Columns.Add(new DataColumn(pi.Name));
        }
        foreach (var o in objects)
        {
            DataRow dr = dt.NewRow();
            foreach (DataColumn dc in dt.Columns)
            {
                dr[dc.ColumnName] = o.GetType().GetProperty(dc.ColumnName).GetValue(o, null);
            }
            dt.Rows.Add(dr);
        }
        return dt;
    }
    return null;
}
Shams Tech
  • 105
  • 8
Mohsin Khan
  • 175
  • 11
0

This is the simple Console Application to convert List to Datatable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;

namespace ConvertListToDataTable
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            List<MyObject> list = new List<MyObject>();
            for (int i = 0; i < 5; i++)
            {
                list.Add(new MyObject { Sno = i, Name = i.ToString() + "-KarthiK", Dat = DateTime.Now.AddSeconds(i) });
            }

            DataTable dt = ConvertListToDataTable(list);
            foreach (DataRow row in dt.Rows)
            {
                Console.WriteLine();
                for (int x = 0; x < dt.Columns.Count; x++)
                {
                    Console.Write(row[x].ToString() + " ");
                }
            }
            Console.ReadLine();
        }

        public class MyObject
        {
            public int Sno { get; set; }
            public string Name { get; set; }
            public DateTime Dat { get; set; }
        }

        public static DataTable ConvertListToDataTable<T>(this List<T> iList)
        {
            DataTable dataTable = new DataTable();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor propertyDescriptor = props[i];
                Type type = propertyDescriptor.PropertyType;

                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    type = Nullable.GetUnderlyingType(type);

                dataTable.Columns.Add(propertyDescriptor.Name, type);
            }
            object[] values = new object[props.Count];
            foreach (T iListItem in iList)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(iListItem);
                }
                dataTable.Rows.Add(values);
            }
            return dataTable;
        }
    }
}
Karthikeyan P
  • 1,216
  • 1
  • 20
  • 23
0
 Dim counties As New List(Of County)
 Dim dtCounties As DataTable
 dtCounties = _combinedRefRepository.Get_Counties()
 If dtCounties.Rows.Count <> 0 Then
    For Each row As DataRow In dtCounties.Rows
      Dim county As New County
      county.CountyId = row.Item(0).ToString()
      county.CountyName = row.Item(1).ToString().ToUpper()
      counties.Add(county)
    Next
    dtCounties.Dispose()
 End If
JoshYates1980
  • 3,476
  • 2
  • 36
  • 57
0

I think it's more convenient and easy to use.

   List<Whatever> _lobj= new List<Whatever>(); 
    var json = JsonConvert.SerializeObject(_lobj);
                DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
Majedur
  • 3,074
  • 1
  • 30
  • 43
0

If you want to use reflection and set columns order/ include only some columns/ Exclude some columns try this:

        private static DataTable ConvertToDataTable<T>(IList<T> data, string[] fieldsToInclude = null,
string[] fieldsToExclude = null)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
        {
            if ((fieldsToInclude != null && !fieldsToInclude.Contains(prop.Name)) ||
                (fieldsToExclude != null && fieldsToExclude.Contains(prop.Name)))
                continue;
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }

        foreach (T item in data)
        {
            var atLeastOnePropertyExists = false;
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {

                if ((fieldsToInclude != null && !fieldsToInclude.Contains(prop.Name)) ||
(fieldsToExclude != null && fieldsToExclude.Contains(prop.Name)))
                    continue;

                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                atLeastOnePropertyExists = true;
            }

            if(atLeastOnePropertyExists) table.Rows.Add(row);
        }


        if (fieldsToInclude != null)
            SetColumnsOrder(table, fieldsToInclude);

        return table;

    }

    private static void SetColumnsOrder(DataTable table, params String[] columnNames)
    {
        int columnIndex = 0;
        foreach (var columnName in columnNames)
        {
            table.Columns[columnName].SetOrdinal(columnIndex);
            columnIndex++;
        }
    }
Ahmed_mag
  • 232
  • 2
  • 6
0
List<object> Basket;

string json = JsonConvert.SerializeObject(Basket, Formatting.Indented);
DataTable dtUsingMethod = Business.GetJSONToDataTableUsingNewtonSoftDll(json);



public static DataTable GetJSONToDataTableUsingNewtonSoftDll(string JSONData)
{
    DataTable dt = (DataTable)JsonConvert.DeserializeObject(JSONData, (typeof(DataTable)));
    return dt;
}
Frankich
  • 842
  • 9
  • 19
  • 3
    Can you provide further explaination of what doing your code and if it needs any dependencies (like NewtonSoft) ? – Frankich Dec 15 '21 at 15:48
0

Here is another one to the list. Cinchoo ETL - an open source library to convert enumerable to datatable.

List<Whatever> whatever = new List<Whatever>();
var dt = whatever.AsDataTable();

Disclaimer: I'm author of this library.

Cinchoo
  • 6,088
  • 2
  • 19
  • 34