I am trying to convert a generic collection (List) to a DataTable. I found the following code to help me do this:
// Sorry about indentation
public class CollectionHelper
{
private CollectionHelper()
{
}
// this is the method I have been using
public static DataTable ConvertTo<T>(IList<T> list)
{
DataTable table = CreateTable<T>();
Type entityType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);
foreach (T item in list)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item);
}
table.Rows.Add(row);
}
return table;
}
public static DataTable CreateTable<T>()
{
Type entityType = typeof(T);
DataTable table = new DataTable(entityType.Name);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);
foreach (PropertyDescriptor prop in properties)
{
// HERE IS WHERE THE ERROR IS THROWN FOR NULLABLE TYPES
table.Columns.Add(prop.Name, prop.PropertyType);
}
return table;
}
}
My problem is that when I change one of the properties of MySimpleClass to a nullable type, I get the following error:
DataSet does not support System.Nullable<>.
How can I do this with Nullable properties/fields in my class?
Then presumably you'll need to lift them to the non-nullable form, using Nullable.GetUnderlyingType, and perhaps change a few null values to DbNull.Value...
Change the assignment to be:
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
and when adding the columns to be:
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(
prop.PropertyType) ?? prop.PropertyType);
And it works. (?? is the null-coalescing operator; it uses the first operand if it is non-null, else the second operand is evaluated and used)
Well. Since DataSet does not support nullable types, you'd have to check if the property is a generic type, get the generic definition of that type and then get the argument (which is the actual type) using, perhaps, Nullable.GetUnderlyingType. If the value is null, just use DBNull.Value in the DataSet.
If Nullable.GetUnderlyingType() given your prop.PropertyType returns a not-null value, use that as the type of a column. Otherwise, use prop.PropertyType itself.
I know this question is old, but I had the same issue for an extension method I made. Using the response from Marc Gravell, I was able to modify my code. This extension method will handle lists of primitive types, strings, enumerations and objects with primitive properties.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
/// <summary>
/// Converts a List<T> to a DataTable.
/// </summary>
/// <typeparam name="T">The type of the list collection.</typeparam>
/// <param name="list">List instance reference.</param>
/// <returns>A DataTable of the converted list collection.</returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
var entityType = typeof (T);
// Lists of type System.String and System.Enum (which includes enumerations and structs) must be handled differently
// than primitives and custom objects (e.g. an object that is not type System.Object).
if (entityType == typeof (String))
{
var dataTable = new DataTable(entityType.Name);
dataTable.Columns.Add(entityType.Name);
// Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
foreach (T item in list)
{
var row = dataTable.NewRow();
row[0] = item;
dataTable.Rows.Add(row);
}
return dataTable;
}
else if (entityType.BaseType == typeof (Enum))
{
var dataTable = new DataTable(entityType.Name);
dataTable.Columns.Add(entityType.Name);
// Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
foreach (string namedConstant in Enum.GetNames(entityType))
{
var row = dataTable.NewRow();
row[0] = namedConstant;
dataTable.Rows.Add(row);
}
return dataTable;
}
// Check if the type of the list is a primitive type or not. Note that if the type of the list is a custom
// object (e.g. an object that is not type System.Object), the underlying type will be null.
var underlyingType = Nullable.GetUnderlyingType(entityType);
var primitiveTypes = new List<Type>
{
typeof (Byte),
typeof (Char),
typeof (Decimal),
typeof (Double),
typeof (Int16),
typeof (Int32),
typeof (Int64),
typeof (SByte),
typeof (Single),
typeof (UInt16),
typeof (UInt32),
typeof (UInt64),
};
var typeIsPrimitive = primitiveTypes.Contains(underlyingType);
// If the type of the list is a primitive, perform a simple conversion.
// Otherwise, map the object's properties to columns and fill the cells with the properties' values.
if (typeIsPrimitive)
{
var dataTable = new DataTable(underlyingType.Name);
dataTable.Columns.Add(underlyingType.Name);
// Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
foreach (T item in list)
{
var row = dataTable.NewRow();
row[0] = item;
dataTable.Rows.Add(row);
}
return dataTable;
}
else
{
// TODO:
// 1. Convert lists of type System.Object to a data table.
// 2. Handle objects with nested objects (make the column name the name of the object and print "system.object" as the value).
var dataTable = new DataTable(entityType.Name);
var propertyDescriptorCollection = TypeDescriptor.GetProperties(entityType);
// Iterate through each property in the object and add that property name as a new column in the data table.
foreach (PropertyDescriptor propertyDescriptor in propertyDescriptorCollection)
{
// Data tables cannot have nullable columns. The cells can have null values, but the actual columns themselves cannot be nullable.
// Therefore, if the current property type is nullable, use the underlying type (e.g. if the type is a nullable int, use int).
var propertyType = Nullable.GetUnderlyingType(propertyDescriptor.PropertyType) ?? propertyDescriptor.PropertyType;
dataTable.Columns.Add(propertyDescriptor.Name, propertyType);
}
// Iterate through each object in the list adn add a new row in the data table.
// Then iterate through each property in the object and add the property's value to the current cell.
// Once all properties in the current object have been used, add the row to the data table.
foreach (T item in list)
{
var row = dataTable.NewRow();
foreach (PropertyDescriptor propertyDescriptor in propertyDescriptorCollection)
{
var value = propertyDescriptor.GetValue(item);
row[propertyDescriptor.Name] = value ?? DBNull.Value;
}
dataTable.Rows.Add(row);
}
return dataTable;
}
}
Here's a version with some modifications to allow for nulls and '\0' characters without blowing up the DataTable.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Data;
namespace SomeNamespace
{
public static class Extenders
{
public static DataTable ToDataTable<T>(this IEnumerable<T> collection, string tableName)
{
DataTable tbl = ToDataTable(collection);
tbl.TableName = tableName;
return tbl;
}
public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
{
DataTable dt = new DataTable();
Type t = typeof(T);
PropertyInfo[] pia = t.GetProperties();
object temp;
DataRow dr;
for (int i = 0; i < pia.Length; i++ )
{
dt.Columns.Add(pia[i].Name, Nullable.GetUnderlyingType(pia[i].PropertyType) ?? pia[i].PropertyType);
dt.Columns[i].AllowDBNull = true;
}
//Populate the table
foreach (T item in collection)
{
dr = dt.NewRow();
dr.BeginEdit();
for (int i = 0; i < pia.Length; i++)
{
temp = pia[i].GetValue(item, null);
if (temp == null || (temp.GetType().Name == "Char" && ((char)temp).Equals('\0')))
{
dr[pia[i].Name] = (object)DBNull.Value;
}
else
{
dr[pia[i].Name] = temp;
}
}
dr.EndEdit();
dt.Rows.Add(dr);
}
return dt;
}
}
}
Related
I'm trying to convert a gridview datasource to a datatable.
What I have tried so far
dt = (DataTable)GridCanvas.DataSource; // Unable to cast object of type 'System.Collections.Generic.List`1 to type 'System.Data.DataTable'
Also I have tried this
Unable to cast object of type 'System.Collections.Generic.List`1[CRM.Models.Leads]' to type 'System.Windows.Forms.BindingSource'
BindingSource bindingSource = (BindingSource)GridCanvas.DataSource;
dt = (DataTable)bindingSource.DataSource;
And this
'Object reference not set to an instance of an object.'
dt = GridCanvas.DataSource as DataTable;
I'm populating my gridview in the following way
var dispatchLeads = await API.Zelkon.Leads.Dispatch.Leads(Variables.Agent.username);
GridCanvas.DataSource = dispatchLeads;
I'm trying to avoid loop solutions. Hope someone has an idea how to solve this. Thanks!
First get the List<Leads> from GridCanvas as
List<Leads> data=(List<Leads>)GridCanvas.DataSource;
Then Convert the List<Leads> to DataTable as;
DataTable dt=ToDataTable<Leads>(data);
use following methods for conversion.
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;
}
When using expando objects to fill my datatable, I am facing following error message when I initialize a new datarow. The exception is:
Invalid Storage Type: DBNull
public static DataTable ToCLDataTable<T>(List<T> items)
{
DataTable dataTable = new DataTable("CLWorkQueue");
//Get all the properties
DataRow dr;
var expandoDict = items[0] as IDictionary<string, object>;
foreach (var key in expandoDict.Keys)
{
if ((expandoDict[key]).GetType() == typeof(DateTime))
{
dataTable.Columns.Add(key.ToString(), typeof(DateTime));
}
else
{
dataTable.Columns.Add(key.ToString(), expandoDict[key].GetType());
}
}
for (int i = 0; i < items.Count; i++)
{
var expandoDictData = items[i] as IDictionary<string, object>;
//var values = new object[expandoDictData.Count];
int j = 0;
dr = dataTable.NewRow(); /*Though the datatable has all the required columns, but whenever i try to initialize a new row an exception occurs.*/
foreach (var key in expandoDictData.Keys)
{
//values[j] = expandoDictData[key].ToString();
dr[key] = expandoDictData[key].ToString();
j++;
}
dataTable.Rows.Add(dr);
//dataTable.Rows.Add(values);
}[enter image description here][1]
The value of the first result in your items[0] was DBNull.Vaue - which is what your SQL client populated into the structure. That's what the SQL client uses to indicate a null value. So, the actual type of the object in the first Value of the IDictionary<String,object> is DBNull...not the type it would have been if there had been a value. You might have to iterate your dictionary until you get a value before you can add that column to the datarow...or come up with a more bulletproof way of assigning the type to the column.
How to convert datatable into list of generic type. Below is the scenario.
I have datatable with name table1 and contains columns col1,col2. how could we convert this table into a list of type name table1bj(which can be different per the datatable name) with properties col1 and col2 with compatible datatype as of datatable column data types.
There are many post on SO but these are with the converting datatable into predefined object list. Here in my case I have to generate object and list dynamically from the datatable. Thanks.
Assuming that you've already created the class table1bj (consider to make it uppercase due to .NET naming conventions) with two properties col1,col2 (the same). You just have to use Enumerable.Select to create instances of this class and ToList to create a generic List<table1bj>:
List<table1bj> result = table1.AsEnumerable()
.Select(row => new table1bj
{
col1 = row.Field<string>("col1"),
col1 = row.Field<string>("col1")
}
).ToList();
I have also presumed that these properties are strings, otherwise use the correct type with the Field extension method. If you don't know the type you should stay with your DataTable since it's already an in-memory collection with dynamic types.
You can do like this...
Create Class with properties :
public class table1bj
{
public string col1{ get; set; }
public string col2{ get; set; }
}
Convert DataTable to Generic Type :
List<table1bj> Objtable1bj = table1.ToCollection<table1bj>();
I know this question asked many times ago, but also I need a solutions for convert data table to dynamic or generic types in one method and I can't find answer for this, so post my answer.
You can use a extension method to convert data table to any type like below:
public static class Extension
{
public static IList<T> ToList<T>(this DataTable dt, bool isFirstRowColumnsHeader = false) where T : new()
{
var results = new List<T>();
if (dt != null && dt.Rows.Count > 0)
{
var columns = dt.Columns.Cast<DataColumn>().ToList();
var rows = dt.Rows.Cast<DataRow>().ToList();
var headerNames = columns.Select(col => col.ColumnName).ToList();
//
// Find properties name or columns name
if (isFirstRowColumnsHeader)
{
for (var i = 0; i < headerNames.Count; i++)
{
if (rows[0][i] != DBNull.Value && !string.IsNullOrEmpty(rows[0][i].ToString()))
headerNames[i] = rows[0][i].ToString();
}
//
// remove first row because that is header
rows.RemoveAt(0);
}
// Create dynamic or anonymous object for `T type
if (typeof(T) == typeof(System.Dynamic.ExpandoObject) ||
typeof(T) == typeof(System.Dynamic.DynamicObject) ||
typeof(T) == typeof(System.Object))
{
var dynamicDt = new List<dynamic>();
foreach (var row in rows)
{
dynamic dyn = new ExpandoObject();
dynamicDt.Add(dyn);
for (var i = 0; i < columns.Count; i++)
{
var dic = (IDictionary<string, object>)dyn;
dic[headerNames[i]] = row[columns[i]];
}
}
return (dynamic)dynamicDt;
}
else // other types of `T
{
var properties = typeof(T).GetProperties();
if (columns.Any() && properties.Any())
{
foreach (var row in rows)
{
var entity = new T();
for (var i = 0; i < columns.Count; i++)
{
if (!row.IsNull(columns[i]))
{
typeof(T).GetProperty(headerNames[i])? // ? -> maybe the property by name `headerNames[i]` is not exist in entity then get null!
.SetValue(entity, row[columns[i]] == DBNull.Value ? null : row[columns[i]]);
}
}
results.Add(entity);
}
}
}
}
return results;
}
}
We can do it by Reflection also, this method helps to set ClassObject properties by DataTable:
using System.Reflection;
public void SetObjectProperties(object objClass, DataTable dataTable)
{
DataRow _dataRow = dataTable.Rows[0];
Type objType = objClass.GetType();
List<PropertyInfo> propertyList = new List<PropertyInfo>(objType.GetProperties());
foreach (DataColumn dc in _dataRow.Table.Columns)
{
var _prop = propertyList.Where(a => a.Name == dc.ColumnName).Select(a => a).FirstOrDefault();
if (_prop == null) continue;
_prop.SetValue(objClass, Convert.ChangeType(_dataRow[dc], Nullable.GetUnderlyingType(_prop.PropertyType) ?? _prop.PropertyType), null);
}
}
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;
}
}
}
Is there any way to convert the result of a LINQ expression to a DataTable without stepping through each element?
Credit to this blogger, but I've improved on his algorithm here. Make yourself an extension method:
public static DataTable ToADOTable<T>(this IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// Use reflection to get property names, to create table
// column names
PropertyInfo[] oProps = typeof(T).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
colType = colType.GetGenericArguments()[0];
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
foreach (T rec in varlist)
{
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null);
dtReturn.Rows.Add(dr);
}
return (dtReturn);
}
Usage:
DataTable dt = query.ToADOTable();
Nope there is no way to create it without stepping through each element. The Linq expression is evaluated when needed so it will step through each row (for matching and selection).
I think you should try using DataTable.Select() (MSDN link) method instead as it returns array of DataRow objects that you can add to new table as follows:
var rows = [ORIGINAL DATA TABLE].Select("id>5");
var dtb=[ORIGINAL DATA TABLE].Clone();
foreach(DataRow r in rows)
{
var newRow = dtb.NewRow();
newRow.ItemArray = r.ItemArray;
dtb.Rows.Add(newRow);//I'm doubtful if you need to call this or not
}