How to evaluate a DataColumn expression faster? - c#

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

Related

Problem with filling a Table in c# from a list

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
}

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

Not Having CopyToDatatable() method

i have a linq to dataTable query like this:
`var ShowResult = from r in Result.AsEnumerable()
where Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")) > 60
orderby Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")) descending
select new
{
pascode = r.Field<string>("PAS_CODE"),
melli = r.Field<string>("CODEMELI"),
name = r.Field<string>("NAM"),
family = r.Field<string>("FAMILY"),
bycode = r.Field<string>("BAYGANI"),
jancode = r.Field<string>("CODEJANBAZ"),
darsad = r.Field<int>("DARSAD"),
ostan = r.Field<string>("OSTAN_N"),
vacode = r.Field<string>("VA_CODE"),
moin = r.Field<string>("VA_MOIN"),
onvan = r.Field<string>("TAFZILI"),
aslvam = r.Field<double>("ASLVAM"),
gest = r.Field<double>("GEST"),
//tededGestKol = Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")),
mandeVam = r.Field<double>("MANDE_VAM"),
dPardakht = r.Field<string>("DATE_P")
};`<code>
and i added reference System.Data.DataSetExtentions to use CopyToDataTable() method for showing my query result in a dataGrid view but this method didn,t add to my Inellisence,
I also use the MSDN Sample to use this method but this time i got this error :
"Specified Cast is not valid"
Please help me , what can i do to overcom this problem?
CopyToDataTable() only works when your query returns an IEnumerable<'DataRow>. In your query, you are returning an anonymous type. Anonymous types don't carry the extension method for CopyToDataTable(). You could just select the entire row like this, assuming Result is a DataTable. Then create your anonymous type.
public static void Start()
{
DataTable Result = new DataTable();
var ShowResult = from r in Result.AsEnumerable()
where Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")) > 60
orderby Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")) descending
select r;
DataTable newDataTbl = ShowResult.CopyToDataTable();
var anonType = newDataTbl.AsEnumerable()
.Select(r => new
{
pascode = r.Field<string>("PAS_CODE"),
melli = r.Field<string>("CODEMELI"),
name = r.Field<string>("NAM"),
family = r.Field<string>("FAMILY"),
bycode = r.Field<string>("BAYGANI"),
jancode = r.Field<string>("CODEJANBAZ"),
darsad = r.Field<int>("DARSAD"),
ostan = r.Field<string>("OSTAN_N"),
vacode = r.Field<string>("VA_CODE"),
moin = r.Field<string>("VA_MOIN"),
onvan = r.Field<string>("TAFZILI"),
aslvam = r.Field<double>("ASLVAM"),
gest = r.Field<double>("GEST"),
//tededGestKol = Convert.ToInt32(r.Field<double>("ASLVAM") / r.Field<double>("GEST")),
mandeVam = r.Field<double>("MANDE_VAM"),
dPardakht = r.Field<string>("DATE_P")
}
);
}
In lieu of the former method, you could use the following extension methods to create a Datatable from a List<'T>.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;
using System.Reflection;
namespace Common
{
public static class DataTableExtensions
{
public static DataTable ConvertToDataTable<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);
}
table.AcceptChanges();
return table;
}
public static DataRow ConvertToDataRow<T>(this T item, DataTable table)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
return row;
}
public static T ConvertToEntity<T>(this DataRow tableRow) where T : new()
{
// Create a new type of the entity I want
Type t = typeof(T);
T returnObject = new T();
foreach (DataColumn col in tableRow.Table.Columns)
{
string colName = col.ColumnName;
// Look for the object's property with the columns name, ignore case
PropertyInfo pInfo = t.GetProperty(colName.ToLower(),
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
// did we find the property ?
if (pInfo != null)
{
object val = tableRow[colName];
// is this a Nullable<> type
bool IsNullable = (Nullable.GetUnderlyingType(pInfo.PropertyType) != null);
if (IsNullable)
{
if (val is System.DBNull)
{
val = null;
}
else
{
// Convert the db type into the T we have in our Nullable<T> type
val = Convert.ChangeType(val, Nullable.GetUnderlyingType(pInfo.PropertyType));
}
}
else
{
// Convert the db type into the type of the property in our entity
SetDefaultValue(ref val, pInfo.PropertyType);
if (pInfo.PropertyType.IsEnum && !pInfo.PropertyType.IsGenericType)
{
val = Enum.ToObject(pInfo.PropertyType, val);
}
else
val = Convert.ChangeType(val, pInfo.PropertyType);
}
// Set the value of the property with the value from the db
if (pInfo.CanWrite)
pInfo.SetValue(returnObject, val, null);
}
}
// return the entity object with values
return returnObject;
}
private static void SetDefaultValue(ref object val, Type propertyType)
{
if (val is DBNull)
{
val = GetDefault(propertyType);
}
}
public static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
public static List<T> ConvertToList<T>(this DataTable table) where T : new()
{
Type t = typeof(T);
// Create a list of the entities we want to return
List<T> returnObject = new List<T>();
// Iterate through the DataTable's rows
foreach (DataRow dr in table.Rows)
{
// Convert each row into an entity object and add to the list
T newRow = dr.ConvertToEntity<T>();
returnObject.Add(newRow);
}
// Return the finished list
return returnObject;
}
}
}

Datatable subset of columns from another datatable

I have a datatable with 17 columns and a bunch of data.
I wnat a datatable with only 6 of the columns and the data for those 6 columns.
So I need a subset of the original datatable.
How do I loop through the original datatable with 17 columns and end up with a datatable with only the 6 columns I want with the corresponding data for those 6 columns?
Private Function createSmallCopyofExistingTable(ByVal SourceTable As DataTable) As DataTable
Dim newTable As DataTable = New DataTable()
'Copy Only 6 columns from the datatable
Dim ColumnsToExport() As String = {"ID", "FirstName", "LastName", "DateOfBirth", "City", "State"}
newTable = SourceTable.DefaultView.ToTable("tempTableName", False, ColumnsToExport)
Return newTable
End Function
Without knowing more about how generic this needs to be its really just...
foreach (DataRow dr in dt.Rows)
{
newDt.Rows.Add(dr["col1"],dr["col5"],etc);
}
what about data types, and columns? are these same? if yes, you can create
object[] row = new object[]{// Fill your rows manually};
before filling it create
DataTable dt = new DataTable();
dt.Columns.Add("Title",typeof(string etc..));.....
and finally
dt.Rows.Add(row);
Personally, I would avoid creating another instance of a DataTable.
It depends on your situation, of course, but if this is purely for usability and not for security (i.e. you're not trying to remove columns with sensitive data before transmitting it somewhere), then I would create a wrapper object that encapsulates the columns that you want to expose.
The benefit of using a wrapper is in case you are doing any updates, then you can update the source table directly rather than the copy. Whether this really matters, of course, depends on your situation.
A simple example with limited functionality:
public class MyFormOrPage
{
void UsageExample()
{
DataTable allDataTable = new DataTable();
// populate the data table with whatever logic ...
// wrap the data table to expose only the Name, Address, and PhoneNumber columns
var limitedDataTable = new DataTableWrapper(allDataTable, "Name", "Address", "PhoneNumber");
// iterate over the rows
foreach (var limitedDataRow in limitedDataTable)
{
// iterate over the columns
for (int i = 0; i < limitedDataTable.ColumnCount; i++)
{
object value = limitedDataRow[i];
// do something with the value ...
}
}
// bind the wrapper to a control
MyGridControl.DataSource = limitedDataTable;
}
}
public class DataTableWrapper : IEnumerable<DataRowWrapper>
{
private DataTable _Table;
private string[] _ColumnNames;
public DataTableWrapper(DataTable table, params string[] columnNames)
{
this._Table = table;
this._ColumnNames = columnNames;
}
public int ColumnCount
{
get { return this._ColumnNames.Length; }
}
public IEnumerator<DataRowWrapper> GetEnumerator()
{
foreach (DataRow row in this._Table.Rows)
{
yield return new DataRowWrapper(row, this._ColumnNames);
}
}
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
// if you _really_ want to make a copy of the DataTable, you can use this method
public DataTable CopyToDataTable()
{
DataTable copyTable = new DataTable();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
DataColumn column = this._Table.Columns[index];
copyTable.Columns.Add(column);
}
foreach (DataRow row in this._Table.Rows)
{
DataRow copyRow = copyTable.NewRow();
for (int index = 0; index < this._ColumnNames.Length; index++)
{
copyRow[index] = row[this._ColumnNames[index]];
}
copyTable.Rows.Add(copyRow);
}
return copyTable;
}
}
// let's make this a struct, since potentially very many of these will be instantiated
public struct DataRowWrapper
{
private DataRow _Row;
private string[] _ColumnNames;
public DataRowWrapper(DataRow row, params string[] columnNames)
{
this._Row = row;
this._ColumnNames = columnNames;
}
// use this to retrieve column values from a row
public object this[int index]
{
get { return this._Row[this._ColumnNames[index]]; }
set { this._Row[this._ColumnNames[index]] = value; }
}
// just in case this is still needed...
public object this[string columnName]
{
get { return this._Row[columnName]; }
set { this._Row[columnName] = value; }
}
}

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