Problem with filling a Table in c# from a list - c#

I am working with WindowsForm and C# and I am filling out a List<> and everything is fine so far
In my list<> I have 103 records but when I pass to my DataTable no record appears only the headers, that is to say that when I receive my table everything shows in null
The truth is, I'm a little new to this and it's the first one that I'm working on.
List<MSProject.Task> tasks = new List<MSProject.Task>();
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
my code
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");
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;
}
Thanks in advance

I've run your code and it's work, The problem I believe is that the MSProject.Task doesn't have any public properties and you are iterating only in properties. PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
see the code below:
//with properties
public class MSProject.Task
{
public string Name {get; set;}
public string code {get; set;}
}
//no properties
public class MSProject.Task2
{
public string Name;
public string code;
}
private void ShowRownsInGrid()
{
//it works shows rows in DataGridView!
List<MSProject.Task> tasks = new List<MSProject.Task>()
{
new MSProject.Task(){ Name = "Joao", code = "01215452124"},
new MSProject.Task(){ Name = "Maria", code = "4564678"},
};
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
MessageBox.Show(Tabla.Rows.Count.ToString()); //Show '2'
dataGridView1.DataSource = Tabla; //Show 2 rows
}
private void DontShowRownsInGrid()
{
//it doesn't work, no rows are displayed in the DataGridView!
List<MSProject.Task2> tasks = new List<MSProject.Task2>()
{
new MSProject.Task2(){ Name = "Joao", code = "01215452124"},
new MSProject.Task2(){ Name = "Maria", code = "4564678"},
};
DataTable Tabla = new DataTable();
Tabla = ListToDataTable(tasks);
MessageBox.Show(Tabla.Rows.Count.ToString()); //Show '2'
dataGridView1.DataSource = Tabla; //does not show lines
}

Related

How to evaluate a DataColumn expression faster?

I created a class that's responsible to evaluate an expression values for the row when I need it but the problem that when my methods works very slow in big datatables.
public static class RowExpressionEvaluator
{
public static object EvaluateValue(DataRow row, string expression, Type outputType)
{
if (row == null) throw new ArgumentNullException(nameof(row));
return EvaluateValue(row.Table, row, expression, outputType);
}
private static object EvaluateValue(DataTable table, DataRow row, string expression, Type outputType)
{
if (table == null) throw new ArgumentNullException(nameof(table));
if (row == null) throw new ArgumentNullException(nameof(row));
if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Expression cannot be null or empty.", nameof(expression));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(#"The rowState is detached.");
object result = null;
using (var tempColumn = new DataColumn("Exp", outputType))
{
table.Columns.Add(tempColumn);
tempColumn.Expression = expression;
if (!row[tempColumn.ColumnName].IsDbNull())
result = row[tempColumn.ColumnName];
table.Columns.Remove(tempColumn);
}
return result;
}
}
This code works fine but its slow when the datatable contains big data
I tried to improve this code so when I have many expressions to evaluate
then when I finish I dispose it it works better but still needs to improve because I think when I add the column with an expression the datatable evaluate it for all the rows but I need to evaluate the value only for the passed row.
like this one :
public sealed class BetterRowExpressionEvaluator :IDisposable
{
private readonly DataRow _row;
private readonly DataColumn _expressionColumn;
public BetterRowExpressionEvaluator(DataRow row)
{
_row = row ?? throw new ArgumentNullException(nameof(row));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException(#"The rowState is detached.");
_expressionColumn = new DataColumn("Expression",typeof(object));
DataTable table = _row.Table;
table.Columns.Add(_expressionColumn);
}
public object Evaluate(string expression)
{
if (string.IsNullOrEmpty(expression)) throw new ArgumentException("Value cannot be null or empty.", nameof(expression));
_expressionColumn.Expression = expression;
return !_row[_expressionColumn.ColumnName].IsDbNull() ? _row[_expressionColumn.ColumnName] : null;
}
public void Dispose()
{
if (_expressionColumn == null) return;
DataTable table = _row.Table;
table.Columns.Remove(_expressionColumn);
_expressionColumn?.Dispose();
}
}
I did something like this to clone the row and it's parent and child relations so when I evaluate the expressions it comes faster and this is what I did :
private DataSet CloneTableWithRelations(DataRow row)
{
var dataset = new DataSet("EvaluationDataSet") {Locale = CultureInfo.InvariantCulture};
dataset.Tables.Add(row.Table.Clone());
dataset.Tables[row.Table.TableName].ImportRow(row);
foreach (DataRelation parentRelation in row.Table.ParentRelations)
{
string relationName = parentRelation.RelationName;
DataTable parentTable = parentRelation.ParentTable;
// clone the parent table
dataset.Tables.Add(parentTable.Clone());
// copy the parent rows related only to the passed row
DataRow parentRow= row.GetParentRow(relationName);
dataset.Tables[parentTable.TableName].ImportRow(parentRow);
DataColumn parentColumn=parentRelation.ParentColumns[0];
DataColumn childColumn=parentRelation.ChildColumns[0];
dataset.Relations.Add(relationName, parentColumn, childColumn,false);
}
foreach (DataRelation dataRelation in row.Table.ChildRelations)
{
DataTable childTable = dataRelation.ChildTable;
// clone the parent table
dataset.Tables.Add(childTable.Clone());
// copy the parent rows related only to the passed row
foreach (DataRow childRow in row.GetChildRows(dataRelation.RelationName))
{
dataset.Tables[childTable.TableName].ImportRow(childRow);
}
DataColumn parentColumn=dataRelation.ParentColumns[0];
DataColumn childColumn=dataRelation.ChildColumns[0];
dataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn,false);
}
return dataset;
}
is there a better way to do it and more reliable way ?
Finally It works much better
when I clone the row and its parent and child data
using this class I've created
public class RowCloneHandler
{
private readonly DataRow _row;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "<Pending>")]
public RowCloneHandler(DataRow row)
{
_row = row ?? throw new ArgumentNullException(nameof(row));
if (row.RowState.Equals(DataRowState.Detached)) throw new ArgumentException("The rowState is detached.");
}
public DataSet CloneToDataSet()
{
var ClonedDataset = new DataSet { Locale = CultureInfo.InvariantCulture };
DataTable clonedMainTable = _row.Table.Clone();
ClonedDataset.Tables.Add(clonedMainTable);
ClonedDataset.Tables[_row.Table.TableName].ImportRow(_row);
CloneParentTablesToDataset(ClonedDataset, clonedMainTable);
CloneChildTablesToDataSet(ClonedDataset, clonedMainTable);
return ClonedDataset;
}
private void CloneChildTablesToDataSet(DataSet clonedDataset, DataTable clonedMainTable)
{
foreach (DataRelation dataRelation in _row.Table.ChildRelations)
{
DataTable childTable = dataRelation.ChildTable;
// clone the parent table
DataTable clonedChildTable = childTable.Clone();
// copy the parent rows related only to the passed row
foreach (DataRow childRow in _row.GetChildRows(dataRelation.RelationName))
{
clonedChildTable.ImportRow(childRow);
}
clonedDataset.Tables.Add(clonedChildTable);
DataColumn parentColumn = clonedMainTable.Columns[dataRelation.ParentColumns[0].ColumnName];
DataColumn childColumn = clonedChildTable.Columns[dataRelation.ChildColumns[0].ColumnName];
clonedDataset.Relations.Add(dataRelation.RelationName, parentColumn, childColumn, false);
}
}
private void CloneParentTablesToDataset(DataSet clonedDataset, DataTable clonedMainTable)
{
foreach (DataRelation parentRelation in _row.Table.ParentRelations)
{
DataTable parentTable = parentRelation.ParentTable;
// clone the parent table
DataTable clonedParentTable = parentTable.Clone();
// copy the parent rows related only to the passed row
DataRow parentRow = _row.GetParentRow(parentRelation.RelationName);
clonedParentTable.ImportRow(parentRow);
clonedDataset.Tables.Add(clonedParentTable);
DataColumn parentColumn = clonedParentTable.Columns[parentRelation.ParentColumns[0].ColumnName];
DataColumn childColumn = clonedMainTable.Columns[parentRelation.ChildColumns[0].ColumnName];
clonedDataset.Relations.Add(parentRelation.RelationName, parentColumn, childColumn, false);
}
}
}
You need to create a clone of your DataTable, import the DataRow to the cloned DataTable, and then add the computed column in the cloned DataTable. Here are some extension methods that do exactly that.
Update: I revised the code to take into account the existing relations with other tables. The code became much more complicated because now the cloned table must be placed inside the existing DataSet, and the existing relations must be also cloned, and temporary renamed. The cloned relations are created without constraints, so hopefully the performance will not be negatively affected.
public static class DataRowExtensions
{
public static object Compute(this DataRow dataRow, string expression)
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(object), expression);
return clonedRow[dataColumn];
}
}
public static T Compute<T>(this DataRow dataRow, string expression)
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
return clonedRow.Field<T>(dataColumn);
}
}
public static T? ComputeNullable<T>(this DataRow dataRow, string expression)
where T : struct
{
using (var clonedDT = CloneDataTable(dataRow))
{
clonedDT.ImportRow(dataRow);
var clonedRow = clonedDT.Rows[0];
var dataColumn = clonedDT.Columns.Add(null, typeof(T), expression);
return clonedRow.Field<T?>(dataColumn);
}
}
private static DataTable CloneDataTable(DataRow dataRow)
{
var dataTable = dataRow.Table;
var dataSet = dataRow.Table.DataSet;
if (dataSet == null) return dataTable.Clone();
var clonedDT = dataSet.Tables.Add();
foreach (DataColumn column in dataTable.Columns)
{
clonedDT.Columns.Add(column.ColumnName, column.DataType);
}
var relationsAdded = new List<
(DataRelation Cloned, DataRelation Original)>();
foreach (var relation in dataTable.ParentRelations
.Cast<DataRelation>().ToArray())
{
var relationName = relation.RelationName;
relation.RelationName = Guid.NewGuid().ToString();
var clonedColumns = relation.ChildColumns
.Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
var clonedRelation = dataSet.Relations.Add(relationName,
relation.ParentColumns, clonedColumns, createConstraints: false);
relationsAdded.Add((clonedRelation, relation));
}
foreach (var relation in dataTable.ChildRelations
.Cast<DataRelation>().ToArray())
{
var relationName = relation.RelationName;
relation.RelationName = Guid.NewGuid().ToString();
var clonedColumns = relation.ParentColumns
.Select(c => clonedDT.Columns[c.ColumnName]).ToArray();
var clonedRelation = dataSet.Relations.Add(relationName,
clonedColumns, relation.ChildColumns, createConstraints: false);
relationsAdded.Add((clonedRelation, relation));
}
clonedDT.Disposed += (s, e) => // Cleanup
{
clonedDT.Rows.Clear();
foreach (var entry in relationsAdded)
{
dataSet.Relations.Remove(entry.Cloned);
entry.Original.RelationName = entry.Cloned.RelationName;
}
clonedDT.Columns.Clear();
dataSet.Tables.Remove(clonedDT);
};
return clonedDT;
}
}
Usage example:
var dt = new DataTable();
dt.Columns.Add("Price", typeof(decimal));
dt.Rows.Add(10);
decimal doublePrice = dt.Rows[0].Compute<decimal>("Price * 2");
Console.WriteLine(doublePrice);
Output:
20

How to convert nested List to Dataset in C#

I have a nested list and I need to convert it to DataSet in C#. I found many example about this but they dont do what I need. I have a list in a list, I need the nested list in another DataTable in the DataSet.
Here is an example of list
public class InvoiceResponse(){
public string BillToName { get; set; }
public string BillToAddr1 { get; set; }
...
...
public List<InvoiceItemResponse> Items { get; set; }
}
I have used the code below to convert the List to DataSet but It didnt convert Items to DataTable in DataSet
public DataSet CreateDataSet<T>(List<T> list)
{
//list is nothing or has nothing, return nothing (or add exception handling)
if (list == null || list.Count == 0) { return null; }
//get the type of the first obj in the list
var obj = list[0].GetType();
//now grab all properties
var properties = obj.GetProperties();
//make sure the obj has properties, return nothing (or add exception handling)
if (properties.Length == 0) { return null; }
//it does so create the dataset and table
var dataSet = new DataSet();
var dataTable = new DataTable();
//now build the columns from the properties
var columns = new DataColumn[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
columns[i] = new DataColumn(properties[i].Name, properties[i].PropertyType);
}
//add columns to table
dataTable.Columns.AddRange(columns);
//now add the list values to the table
foreach (var item in list)
{
//create a new row from table
var dataRow = dataTable.NewRow();
//now we have to iterate thru each property of the item and retrieve it's value for the corresponding row's cell
var itemProperties = item.GetType().GetProperties();
for (int i = 0; i < itemProperties.Length; i++)
{
dataRow[i] = itemProperties[i].GetValue(item, null);
}
//now add the populated row to the table
dataTable.Rows.Add(dataRow);
}
//add table to dataset
dataSet.Tables.Add(dataTable);
//return dataset
return dataSet;
}
How can I convert the Items list to another DataTable into the DataSet?
You can just add a bit of recursivity to your code, something like this:
var set = new DataSet();
AddToDataSet(set, resp);
...
public static void AddToDataSet(DataSet set, object value)
{
if (set == null)
throw new ArgumentNullException(nameof(set));
if (value == null)
return;
var type = value.GetType();
var table = set.Tables[type.FullName];
if (table == null)
{
// create one table per type (name)
table = new DataTable(type.FullName);
set.Tables.Add(table);
foreach (var prop in type.GetProperties().Where(p => p.CanRead))
{
if (IsEnumerable(prop))
continue;
var col = new DataColumn(prop.Name, prop.PropertyType);
table.Columns.Add(col);
}
}
var row = table.NewRow();
foreach (var prop in type.GetProperties().Where(p => p.CanRead))
{
object propValue = prop.GetValue(value);
if (IsEnumerable(prop))
{
if (propValue != null)
{
foreach (var child in (ICollection)propValue)
{
AddToDataSet(set, child);
}
}
continue;
}
row[prop.Name] = propValue;
}
table.Rows.Add(row);
}
private static bool IsEnumerable(PropertyInfo pi)
{
// note: we could also use IEnumerable (but string, arrays are IEnumerable...)
return typeof(ICollection).IsAssignableFrom(pi.PropertyType);
}

Convert dynamic list to datatable c#

am searching and cracking my brain on how to convert a dynamic list to a databale,
c#, please advise, thanks
List<dynamic>dlist=new List<dynamic>
to
DataTable
I think you looking something like this. Hope it's working for you.
From dynamic list to DataTable:
List<dynamic> dlist=new List<dynamic>
var json = JsonConvert.SerializeObject(dlist);
DataTable dataTable = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
Also to get JsonString from DataTable:
string JSONresult = JsonConvert.SerializeObject(dataTable);
The following is the method through which you can convert any list object to datatable..
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;
}
http://social.msdn.microsoft.com/Forums/vstudio/en-US/6ffcb247-77fb-40b4-bcba-08ba377ab9db/converting-a-list-to-datatable?forum=csharpgeneral
public DataTable ToDataTable<T>(dynamic items)
{
DataTable dtDataTable = new DataTable();
if (items.Count == 0) return dtDataTable;
((IEnumerable)items[0]).Cast<dynamic>().Select(p => p.Name).ToList().ForEach(col => { dtDataTable.Columns.Add(col); });
((IEnumerable)items).Cast<dynamic>().ToList().
ForEach(data =>
{
DataRow dr = dtDataTable.NewRow();
((IEnumerable)data).Cast<dynamic>().ToList().ForEach(Col => { dr[Col.Name] = Col.Value; });
dtDataTable.Rows.Add(dr);
});
return dtDataTable;
}
I don't know why you need this, however, you can use this ObjectShredder using reflection which can convert anything to DataTable, so even dynamic or anonymous types:
Implement CopyToDataTable<T> Where the generic Type T Is Not a DataRow
However, my suggestion is to not name that extension method CopyToDataTable but for example CopyAnyToDataTable to avoid conflicts with the existing extension method CopyToDataTable.
Use this function ,
public static DataTable ConvertToDatatable<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;
}
If the underlying type is ExpandoObject, then you need to check for IDictionary instead of going via reflection. Hopefully this helps someone else in the future:
public static DataTable ConvertToDataTable<T>(IEnumerable<T> data)
{
DataTable table = new DataTable();
foreach (T item in data)
{
if (item is IDictionary<string, object> dict)
{
foreach (var key in dict)
{
table.Columns.Add(key.Key, key.Value?.GetType() ?? typeof(object));
}
break;
}
foreach (var prop in typeof(T).GetProperties())
{
table.Columns.Add(prop.Name, prop.PropertyType);
}
break;
}
DataRow row = null;
foreach (T item in data)
{
if (item is IDictionary<string, object> dict)
{
row = table.NewRow();
foreach (var key in dict)
{
row[key.Key] = key.Value;
}
table.Rows.Add(row);
continue;
}
row = table.NewRow();
foreach (var prop in typeof(T).GetProperties())
{
row[prop.Name] = prop.GetValue(item);
}
table.Rows.Add(row);
}
return table;
}
To Convert the Dynamic List object into DataTable using C#
public DataTable DynamicToDT(List<object> objects)
{
DataTable dt = new DataTable("StudyRecords"); // Runtime Datatable
string[] arr = { "Name", "Department", "CollegeName", "Address" };// Column Name for DataTable
if (objects != null && objects.Count > 0)
{
for (int i = 0; i < objects.Count; i++)
{
dt.Columns.Add(arr[i]);
if (i == 0)
{
var items = objects[0] as IEnumerable<string>;
foreach (var itm in items)
{
DataRow dr1 = dt.NewRow(); // Adding values to Datatable
dr1[arr[i]] = itm;
dt.Rows.Add(dr1);
}
}
else
{
var items = objects[i] as IEnumerable<string>;
int count = 0;
foreach (var itm in items)
{
dt.Rows[count][arr[i]] = itm;
count++;
}
}
}
return dt; // Converted Dynamic list to Datatable
}
return null;
}
public static DataTable DynamicToDT(List objects)
{
var data = objects.ToArray();
if (data.Count() == 0) return null;
var dt = new DataTable();
foreach (var key in ((IDictionary<string, object>)data[0]).Keys)
{
dt.Columns.Add(key);
}
foreach (var d in data)
{
dt.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
}
return dt;
}
// Obtem a lista dinamica via chamada API
List<dynamic> resultado = ExecutaQuery(sql);
// converte a lista dinamica com o resultado em JSON
string json = JsonConvert.SerializeObject(resultado);
// converte o json em datatable
DataTable dataTable = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));

extracting contents of Datatable into list C#

I am experimenting to take datatable contents into a list. I am using following code but its not working correctly.
public List<object> ShowMessage()
{
List<object> obj = new List<object>();
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Rows.Add("1","AAA");
dt.Rows.Add("2", "BBB");
dt.Rows.Add("3", "CCC");
foreach (DataRow dr in dt.Rows)
{
obj.Add(dr);
}
return obj;
}
I am new and not sure I am doing in a right way or I need to use some thing else. Any suggestion will be highly appreciated.
Thanks.
Converting your DataTable to list of name strings (with help of Linq to DataSet):
List<string> names =
dt.AsEnumerable().Select(r => r.Field<string>("Name")).ToList();
Which is same as
List<string> names = new List<string>();
foreach(DataRow r in dt.Rows)
names.Add((string)r["Name"]);
I think you are making a too abstract example. This one use a possible class named Person
public class Person
{
public int PersonID;
public string Name;
// other fields will follow in future
}
public List<Person> GetPersonList()
{
List<Person> people = new List<Person>();
// This is just as example, because in real code
// you get this table from a database
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Rows.Add(1,"John");
dt.Rows.Add(2, "Mark");
dt.Rows.Add(3, "Steve");
// Loop over the rows and construct a Person instance for every row
// Add that row to the List<Person> to return
foreach (DataRow dr in dt.Rows)
{
Person p = new Person() {PersonID =Convert.ToInt32(dr[0]), Name = dr[1].ToString());
people.Add(p);
}
return people;
}
By the way, this pattern of code, is exactly what a good ORM do for you. A little research for Entity Framework or Dapper would be very useful
#Karni: you were close to what you need. however below is the modified version of your code example so that you achieve what you need..
public class Obj
{
public int ID { get; set; }
public string Name { get; set; }
}
public class ListObj : List<Obj>
{
}
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Rows.Add("1", "AAA");
dt.Rows.Add("2", "BBB");
dt.Rows.Add("3", "CCC");
ListObj objListObj = new ListObj();
//to fill the list / collection
for (int i = 0; i < dt.Rows.Count; i++)
{
objListObj.Add(new Obj() { ID = Convert.ToInt16(dt.Rows[i][0]), Name = dt.Rows[i][1].ToString() });
}
//To verify if the collection is filled.
foreach (var item in objListObj)
{
Console.WriteLine(item.ID + " : " + item.Name);
}
Console.Read();
}
}

Convert generic List/Enumerable to DataTable?

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;
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
}
}
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;
}
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 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;
}
Another approach is the above:
List<WhateEver> lst = getdata();
string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
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();
List<YourModel> data = new List<YourModel>();
DataTable dataTable = Newtonsoft.Json.JsonConvert.DeserializeObject<DataTable>(Newtonsoft.Json.JsonConvert.SerializeObject(data));
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;
}
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;
}
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.
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
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;
}
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:
ToDataTable() in MoreLinq on GitHub
original: ModelShredder on GoogleCode, was merged into MoreLinq, which moved from GoogleCode to GitHub
A 2019 answer if you're using .NET Core - use the Nuget ToDataTable library. Advantages:
Better performance than FastMember
Also creates structured SqlParameters for use as SQL Server Table-Valued Parameters
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.
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;
}
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;
}
}
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);
}
}
}
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;
}
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
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();
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
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;
}
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;
}
}
}
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
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)));
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++;
}
}
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;
}
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.

Categories

Resources