How to convert a list into data table [duplicate] - c#

This question already has answers here:
Convert generic List/Enumerable to DataTable?
(28 answers)
Closed 9 years ago.
I have a data list with some property. I want to convert that list data into data table. How to convert a list into datable.

Add this function and call it, it will convert List to DataTable.
public static DataTable ToDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Defining type of data column gives proper data table
var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
//Setting column names as Property names
dataTable.Columns.Add(prop.Name, type);
}
foreach (T item in items)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
}
dataTable.Rows.Add(values);
}
//put a breakpoint here and check datatable
return dataTable;
}

you can use this extension method and call it like this.
DataTable dt = YourList.ToDataTable();
public static DataTable ToDataTable<T>(this List<T> iList)
{
DataTable dataTable = new DataTable();
PropertyDescriptorCollection propertyDescriptorCollection =
TypeDescriptor.GetProperties(typeof(T));
for (int i = 0; i < propertyDescriptorCollection.Count; i++)
{
PropertyDescriptor propertyDescriptor = propertyDescriptorCollection[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[propertyDescriptorCollection.Count];
foreach (T iListItem in iList)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = propertyDescriptorCollection[i].GetValue(iListItem);
}
dataTable.Rows.Add(values);
}
return dataTable;
}

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

Related

C# Not able to use CopyToDataTable() function when grouping fields by LINQ & Datatable

Here I am grouping data by LINQ on DataTable but I was trying to use CopyToDataTable() which was not available on my side. please see my code and tell me what I missed in my code for which CopyToDataTable() not available.
DataTable perioddata = ds.Tables[1].AsEnumerable()
.GroupBy(a => new
{
NewPeriod = a.Field<string?>("NewPeriod").ToString(),
PeriodOrder = a.Field<int>("PeriodOrder").ToString().ToInt32()
})
.Select(b => new PeriodDto
{
NewPeriod = b.Key.NewPeriod,
PeriodOrder = b.Key.PeriodOrder
}).CopyToDataTable();
CopyToDataTable works with DataRow which is why you can not use it.
DataTable CopyToDataTable<T>(this IEnumerable<T> source) where T : DataRow
Since you have IEnumerable<PeriodDto>, you can create your own extension method, see: Convert generic List/Enumerable to DataTable?
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;
}
Without using a generic extension method and using the PeriodDto definition as the expected DataRow definition
DataTable perioddata = ds.Tables[1].AsEnumerable()
.GroupBy(a => new
{
NewPeriod = a.Field<string>("NewPeriod").ToString(),
PeriodOrder = a.Field<int>("PeriodOrder")
})
.Select(b => {
DataRow row = ds.Tables[1].NewRow();
row["NewPeriod"] = b.Key.NewPeriod;
row["PeriodOrder"] = b.Key.PeriodOrder;
return row;
}).CopyToDataTable();

Take EF Model and Convert it to Datatable?

I have a EF core model that I need to convert to a data table but I don't want to manually map it my self if possible.
I found this code in a stack answer
private static DataTable CreateDataTable<T>(IEnumerable<T> list)
{
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 list)
{
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;
}
however I have properties that of course reference other classes and I think they are being picked up as I get an error like this.
The type of column 'Category' is not supported. The type is 'Category'
You can go with:
List<PropertyInfo> properties = new List<PropertyInfo>();
foreach (PropertyInfo info in type.GetProperties())
{
if (info.PropertyType.IsValueType || info.PropertyType == typeof(string))
{
dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
properties.Add(info);
}
}
foreach (T entity in list)
{
int i = 0;
object[] values = new object[properties.Count];
foreach (PropertyInfo info in properties)
{
values[i] = properties[i].GetValue(entity);
i++;
}
dataTable.Rows.Add(values);
}
This same foreach you can use on the properties, so remove the for loop..

Navisworks Treeview to Datatable

I want to covert a treeview from a model in navisworks and create a datatable.
I tried to use foreach but no luck.
any help would be Appreciated
I resolved my problem by getting the Descendants and passed this into an IEnumerable
var Descendants = PL.oDoc.Models.First.RootItem.Descendants.Select(x => x);
and then with this method I convert that into an datatable:
public static DataTable DataTable<T>(this IEnumerable<T> items)
{
var tb = new DataTable(typeof(T).Name);
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
tb.Columns.Add(prop.Name, prop.PropertyType);
}
foreach (var item in items)
{
var values = new object[props.Length];
for (var i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
tb.Rows.Add(values);
}
return tb;
}

Extra Columns While Converting Linq Result into Data Table

When I convert a Linq result into a data table using method below, I get some extra columns!!!
private static DataTable LinqQueryToDataTable(IEnumerable<dynamic> v)
{
var firstRecord = v.FirstOrDefault();
if (firstRecord == null)
return null;
PropertyInfo[] infos = firstRecord.GetType().GetProperties();
DataTable table = new DataTable();
foreach (var info in infos)
{
Type propType = info.PropertyType;
if (propType.IsGenericType
&& propType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
table.Columns.Add(info.Name, Nullable.GetUnderlyingType(propType));
}
else
{
table.Columns.Add(info.Name, info.PropertyType);
}
}
DataRow row;
foreach (var record in v)
{
row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
row[i] = infos[i].GetValue(record,null) != null ? infos[i].GetValue(record,null) : DBNull.Value;
}
table.Rows.Add(row);
}
table.AcceptChanges();
return table;
}
For example if Linq result has 5 columns the data table will have 6 or more columns with extra data, I need to skip and delete extra generated columns.
How to do it? Or Am I doing something wrong?
For example if Linq result has 5 columns the data table will have 6 or more columns with extra data
Apparently what you call "Linq result" has these "columns". You can easily check the Linq function that provides that or the parameter v itself with a debugger.
if I connect the result into a grid view everything is OK
First, it's not clear what "grid view" you are talking about. Assuming it's a standard WF or WPF data grid view. As usual with UI, what you see there might not be what you get. The UI components does not use directly reflection as in your code, but System.ComponentModel.TypeDescriptor services. One standard behavior is to suppress properties with BrowsableAttribute = No, which I suspect is your case. But without having your actual Linq result, we can only guess - you are the only one who can check that out.
Anyway, if you are in WF, you can use the following:
private static DataTable LinqQueryToDataTable(IEnumerable<dynamic> v)
{
var firstRecord = v.FirstOrDefault();
if (firstRecord == null)
return null;
var infos = ListBindingHelper.GetListItemProperties(v);
DataTable table = new DataTable();
foreach (PropertyDescriptor info in infos)
{
Type propType = info.PropertyType;
if (propType.IsGenericType
&& propType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
table.Columns.Add(info.Name, Nullable.GetUnderlyingType(propType));
}
else
{
table.Columns.Add(info.Name, info.PropertyType);
}
}
DataRow row;
foreach (var record in v)
{
row = table.NewRow();
for (int i = 0; i < table.Columns.Count; i++)
{
row[i] = infos[i].GetValue(record) ?? DBNull.Value;
}
table.Rows.Add(row);
}
table.AcceptChanges();
return table;
}

How to convert a LINQ query result to a DataTable dynamically?

How to convert a LINQ query result to a DataTable dynamically?
There are solutions where you create another class and specify the column names, but I want the flexibility to change the LINQ structure like column names, column quantities, and have a DataTable generated with the columns names automatically.
Thanks
I've included an extension method that I use with SqlBulkCopy that should do the job, but I'd like to ask why you want to this conversion. There are a very limited number of cases (SqlBulkCopy being one) where a list of objects can't do everything a datatable can. You can use them as binding sources for most controls ... just curious.
public static DataTable toDataTable<T>(this IEnumerable<T> value, List<string> exclusionList)
where T : class
{
var dataTable = new DataTable();
var type = typeof(T);
var properties = type.GetProperties().Where(x => !exclusionList.Contains(x.Name)).ToList();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!propertyType.IsScalar())
continue;
var nullableType = Nullable.GetUnderlyingType(propertyType);
propertyType = nullableType ?? propertyType;
var dataColumn = new DataColumn(propertyInfo.Name, propertyType);
if (nullableType != null)
dataColumn.AllowDBNull = true;
dataTable.Columns.Add(dataColumn);
}
foreach (var row in value)
{
var dataRow = dataTable.NewRow();
foreach (var property in properties)
{
var safeValue = property.GetValue(row, null) ?? DBNull.Value;
dataRow[property.Name] = safeValue;
}
dataTable.Rows.Add(dataRow);
}
return dataTable;
}
Look into the MoreLinq Nuget package. It has a function ToDataTable()
var LinqResults = from ......;
DataTable dt_Results = LinqResults.ToDataTable();
https://code.google.com/p/morelinq/
It has other VERY useful functions as well:
https://code.google.com/p/morelinq/wiki/OperatorsOverview
They key is to use the LINQ query result as its Implemented IList interface.
If you receive the result as a parameter on a method as an IList object, you can access its columns and rows, this way:
var props = item.GetType().GetProperties();
Refer to this example, it's a small class which please note the it just abstracts the creation of the DataTable, and there is a static method inside called "LINQToDataTable" which you should use.
Step 1, create a class called "GridHelper" (uses System.Data for DataTable structure)
public class GridHelper
{
private DataTable baseDt;
public GridHelper(string tableName)
{
baseDt = new DataTable(tableName);
}
public DataTable getDataTable()
{
return baseDt;
}
public object[,] getObjToFill()
{
object[,] obj = new object[baseDt.Columns.Count, 2];
for (int i = 0; i < baseDt.Columns.Count; i++)
{
obj[i, 0] = baseDt.Columns[i].ColumnName;
}
return obj;
}
public void addColumn(string colName, Type valueType)
{
baseDt.Columns.Add(colName, valueType);
}
public void addRow(object[,] values)
{
DataRow newRow = baseDt.NewRow();
for (int i = 0; i < values.Length / 2; i++)
{
bool colFound = false;
for (int j = 0; j < baseDt.Columns.Count; j++)
{
if (baseDt.Columns[j].ColumnName == values[i, 0].ToString())
{
colFound = true;
break;
}
}
if (colFound == false)
{
throw new Exception("The column " + values[i, 0].ToString() + " has not been added yet.");
}
newRow[values[i, 0].ToString()] = values[i, 1];
}
baseDt.Rows.Add(newRow);
}
public static DataTable LINQToDataTable<T>(T objToList) where T : System.Collections.IList
{
GridHelper ghResult = new GridHelper("Report");
foreach (Object item in objToList)
{
var props = item.GetType().GetProperties();
foreach (var prop in props)
{
ghResult.addColumn(prop.Name, typeof(string));
//prop.Name
//prop.GetValue(item)
}
break;
}
object[,] obj = ghResult.getObjToFill();
foreach (Object item in objToList)
{
var props = item.GetType().GetProperties();
int index = 0;
foreach (var prop in props)
{
//ReportValue(prop.Name, prop.GetValue(item, null));
//prop.Name
obj[index, 1] = prop.GetValue(item);
index++;
}
ghResult.addRow(obj);
}
return ghResult.getDataTable();
}
}
Usage:
var listaReporte =
(from t in dbContext.TablaPruebas
select new
{
Name = t.name,
Score = t.score
}
) .ToList();
DataTable dt = Library.GridHelper.LINQToDataTable(listaReporte);
And that is, use your DataTable as you wish, on a GridView or DataGridView

Categories