How to deserialize a datatable to list<T> - c#

Im looking for a library(nuget preferably) that converts a datatable with column names matching a generic object, into a list of that object. For example if i have a class:
public class foo{
public string bar{get;set;}
public int count{get;set;}
}
And a datatable
+---------+-------+
| bar | count |
+---------+-------+
| string1 | 1 |
| string2 | 5 |
+---------+-------+
Be able to call something like
List<foo> foos =DTSerializer.Deserialize<foo>(dt).ToList();

Think what you need is a little reflection magic rather than serialization:
class Program
{
static void Main(string[] args)
{
DataTable table = new DataTable();
table.Columns.Add("foo",typeof(string));
table.Columns.Add("bar",typeof(int));
table.Rows.Add("row1", 1);
table.Rows.Add("row2", 2);
var result = table.MapTableToList<foobar>();
foreach (foobar item in result)
{
Console.WriteLine("{0}:{1}", item.foo, item.bar);
}
}
}
class foobar
{
public string foo { get; set; }
public int bar { get; set; }
}
public static class ExtensionMethods
{
public static List<T> MapTableToList<T>(this DataTable table) where T : new ()
{
List<T> result = new List<T>();
var Type = typeof(T);
foreach (DataRow row in table.Rows)
{
T item = new T();
foreach (var property in Type.GetProperties())
{
property.SetMethod.Invoke(item, new object[] { row[table.Columns[property.Name]] });
}
result.Add(item);
}
return result;
}
}
This will only work if your Column Names match up with the properties though.

Enhance this extension code for nullable and for
public static List<T> MapTableToList<T>(this DataTable table) where T : new()
{
List<T> result = new List<T>();
var Type = typeof(T);
foreach (DataRow row in table.Rows)
{
T item = new T();
foreach (var property in Type.GetProperties())
{
if(table.Columns.IndexOf(property.Name) > -1)
{
if (row[table.Columns[property.Name]] == System.DBNull.Value && property.GetMethod.ReturnType.Name.IndexOf("Nullable") > -1)
{
property.SetMethod.Invoke(item, new object[] { null });
}
else
{
property.SetMethod.Invoke(item, new object[] { row[table.Columns[property.Name]] });
}
}
}
result.Add(item);
}
return result;
}

Related

How do I use reflection to dynamically call a type?

I have a DataGrid and I need to convert it to a DataTable. The problem is that I need To be able to get the type of my DataSource dynamically. The grid DataSource is of type 'Observable' which is a class I have in my project, but my task is to be able to dynamically create the DataTable without needing to specify the type only using the DataSource. How can I generate a method that I can use to place in <T> without getting the error " 'mytype' is a variable but is used like a type" .
Type mytype = Grid_Job.DataSource.GetType();
DataTable dt = CreateDataTable<mytype>((IEnumerable<mytype>)Grid_Job.DataSource);
public 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;
}
My Observable class is
public class Observable
{
public int JobNo { get; set; }
public string JobName { get; set; }
public string JobDescription { get; set; }
public string Job_Type { get; set; }
public string Job_Status { get; set; }
}
Effectively, you need to discard the generics and think in terms of the collection at runtime - discovering the element type itself. For example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Reflection;
static class P
{
static object GetData()
=> new ObservableCollection<Observable>
{
new Observable { JobName = "abc", JobNo = 123 },
new Observable { JobName = "def", JobNo = 456 },
new Observable { JobName = "ghi", JobNo = 789 },
};
static void Main()
{
// note that we don't know *anything* about the data source here
object dataSource = GetData();
DataTable dt = CreateDataTable((IEnumerable)dataSource);
foreach (DataColumn col in dt.Columns)
{
Console.Write(col.ColumnName);
Console.Write("\t");
}
Console.WriteLine();
foreach (DataRow row in dt.Rows)
{
foreach (DataColumn col in dt.Columns)
{
Console.Write(row[col]);
Console.Write("\t");
}
Console.WriteLine();
}
}
public static DataTable CreateDataTable(IEnumerable list)
{
Type type = GetElementType(list.GetType());
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));
}
object[] values = new object[properties.Length];
foreach (object entity in list)
{
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(entity);
}
dataTable.Rows.Add(values);
}
return dataTable;
}
static Type GetElementType(Type type)
{
foreach (Type interfaceType in type.GetInterfaces())
{
if (interfaceType.IsGenericType &&
interfaceType.GetGenericTypeDefinition()
== typeof(IList<>))
{
return type.GetGenericArguments()[0];
}
}
return null;
}
}
public class Observable
{
public int JobNo { get; set; }
public string JobName { get; set; }
public string JobDescription { get; set; }
public string Job_Type { get; set; }
public string Job_Status { get; set; }
}
Note that in some cases you should prefer the TypeDescriptor model over reflection, as this allows runtime definition of properties; this is a niche area and probably doesn't apply to you, but it relevant to know about. As an example, this is how DataTable itself chooses to expose the properties it has as discoverable at runtime to general purpose tools. There are also a few list indirection APIs that need to be considered in those cases.

inherited class properties order

I have 2 classes, where one of them inherits from the other:
public class First
{
public string a{get;set;}
public string b{get;set;}
}
public class Second: First
{
public string c {get;set;}
}
Then i create a List<Second> and fill with some information, and then i export this list to an Excel using EPPlus.
But the "problem" is that the properties of the inherited class(Second) appears first and those of the base class(First) after in the excel:
c | a | b
-------------------
The question is whether i can put the properties of the base class first, or can i say to EPPlus the order of the properties?
EDIT:
I put the code to create the Excel, is very simple
:
using (ExcelPackage package = new ExcelPackage(archivo))
{
ExcelWorksheet ws= package.Workbook.Worksheets.Add("Solds");
{
ws.Cells["A3"].LoadFromCollection(lstSolds);
package.Save();
}
}
You can use attributes and reflexion to select the order of the properties. It also allow to select which properties you want to output or not
public class First {
[Order(1)]
public string a { get; set; }
[Order(2)]
public string b { get; set; }
}
public class Second : First {
[Order(3)]
public string c { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
public class OrderAttribute : Attribute {
public int Order {get; set; }
public OrderAttribute(int order) {
Order = order;
}
}
class Program {
static void Main(string[] args) {
List<Second> list = new List<Second>();
list.Add(new Second {
a = "a",
b = "b",
c = "c"
});
WriteList(list);
}
static void WriteList(List<Second> list) {
PropertyInfo[] properties = typeof(Second).GetProperties();
int row = 3;
int col = 0;
foreach (var item in list) {
Dictionary<int, object> values = new Dictionary<int, object>();
foreach (var pi in properties) {
var orderAttr = pi.GetCustomAttribute(typeof(OrderAttribute)) as OrderAttribute;
if (orderAttr != null) { //this allow to output selective propertes. Those properties without [order] attriburte will not output
values.Add(orderAttr.Order, pi.GetValue(item, null));
}
}
foreach (var key in values.Keys.OrderBy(x => x)) {
ws.Cells[row, col + key].Value = values[key];
}
row++;
}
}
Edited as #Ion comments, added function to read from Excel
static List<Second> ReadFromExcel(string filePath) {
List<Second> result = new List<Second>();
PropertyInfo[] props = typeof(Second).GetProperties();
//Allow access propertyInfo by name
Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
foreach (var pi in props) {
properties.Add(pi.Name, pi);
}
using (var xls = new ExcelPackage(new FileInfo(filePath))) {
ExcelWorksheet ws = xls.Workbook.Worksheets[0];
//Let's assume you have property names has row heading in Excel
List<string> names = new List<string>(3);
for (int i = 1; i <= 4; i++) {
names.Add(ws.Cells[1, i].Value.ToString());
}
//Fill the list from Excel
for (int row = 3; row <= ws.Dimension.End.Row; row++) {
Second second = new Second();
for (int col = 1; col <= 4; col++) {
object value = ws.Cells[row, col].Value;
if (value != null)
properties[names[col]].SetValue(second, value);
}
result.Add(second);
}
}
return result;
}
Another way, more "Epplus" way I guess you could say, would be something like this:
var lstSolds = new List<First>();
for (var i = 0; i < 10; i++)
{
lstSolds.Add(new Second
{
a = "A-" + i,
b = "B-" + i,
c = "C-" + i
});
}
using (ExcelPackage package = new ExcelPackage(archivo))
{
var mi = typeof (Second)
.GetProperties()
.OrderBy(pi => pi.Name) //This controls the column order
.Cast<MemberInfo>()
.ToArray();
ws.Cells["A3"]
.LoadFromCollection(
lstSolds
, true
, TableStyles.Custom
, BindingFlags.Default
, mi
);
package.Save();
}
You can control the order of the columns by adjusting the mi collection order. In this case it is ordered ascending but any order set will be respected by LoadFromCollection.
Response to Comments
Its easy to have them sort in any order you need. Just need to build the array before passing in a way that guarantees sort order. Say we add a property d to the First class. We can make sure it sorts before c like this:
public class First
{
public string a { get; set; }
public string b { get; set; }
public string d { get; set; } //Say we need this to sort before 'c'
}
public class Second : First
{
public string c { get; set; }
}
...
var lstSolds = new List<First>();
for (var i = 0; i < 10; i++)
{
lstSolds.Add(new Second
{
a = "A-" + i,
b = "B-" + i,
c = "C-" + i,
d = "D-" + i,
});
}
using (var package = new ExcelPackage(file))
{
var ws = package.Workbook.Worksheets.Add("table");
//var mi = typeof(Second)
// .GetProperties()
// .OrderBy(pi => pi.Name) //This controls the column order
// .Cast<MemberInfo>()
// .ToArray();
var firstmi = typeof (First)
.GetProperties()
.OrderBy(pi => pi.Name);
var secondmi = typeof (Second)
.GetProperties()
.Where(pi => !firstmi.Select(fpi => fpi.Name).Contains(pi.Name))
.OrderBy(pi => pi.Name);
//Sorting above will keep first proper before second
var mi = firstmi
.Concat(secondmi)
.Cast<MemberInfo>()
.ToArray();
ws.Cells["A3"]
.LoadFromCollection(
lstSolds
, true
, TableStyles.Custom
, BindingFlags.Public | BindingFlags.Instance
, mi
);
package.Save();
}

Invalid Arguments Error Using Generics

I am trying to use the function found here:
DataTable to List<object>
public static IList<T> ConvertTo<T>(DataTable table)
{
if (table == null)
return null;
List<DataRow> rows = new List<DataRow>();
foreach (DataRow row in table.Rows)
rows.Add(row);
return ConvertTo<T>(rows);
}
the return statement is giving me an exception stating:
The best overloaded method match for 'Utilities.Utility.ConvertTo<T>(System.Data.DataTable)' has some invalid arguments
Can someone help me fix this error??
Don't do it that way use Marc's amazing function ( https://stackoverflow.com/a/545429/215752 ) I mean really... that is exactly what you want.. right?
You need another function
public static IList<T> ConvertTo<T>(IList<DataRow> rows)
{
IList<T> list = null;
if (rows != null)
{
list = new List<T>();
foreach (DataRow row in rows)
{
T item = CreateItem<T>(row);
list.Add(item);
}
}
return list;
}
The question you link had a link in it. I'd suggest looking there for more info:
http://lozanotek.com/blog/archive/2007/05/09/Converting_Custom_Collections_To_and_From_DataTable.aspx
There you will find all the code, which I expect will compile. I won't vouch for the reliability or reasonably of using it however.
First, you are returning a T but not a IList<T>. Secondly, how are you expecting a DataRow to be converted to an unknown T, especially if a row has several columns?
Try something like this
public static IList<IList<T>> ConvertTo<T>(DataTable table)
{
if (table == null)
return null;
List<IList<T>> rows = new List<IList<T>>();
foreach (DataRow row in table.Rows) {
rows.Add(row.ItemArray.Cast<T>().ToArray());
}
return rows;
}
UPDATE:
A custom object is something like this
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
}
However, in that case a generic interface is not useful, since you would have to code for that specific class
public static IList<Employee> GetEmployees(DataTable table)
{
var employees = new List<Employee>();
if (table != null) {
foreach (DataRow row in table.Rows) {
var emp = new Employee();
emp.ID = (int)row["ID"];
emp.Name = (string)row["Name"];
emp.Salary = (decimal)row["Salary"];
employees.Add(emp);
}
}
return employees;
}
This code has to be different for different tables and cannot be generic. At least not without using Reflection and assuming that the properties have the same names as the table columns.
A solution not using some tricky Reflection code or other magic tricks would be to define an interface like this
public interface IDataObject
{
void FillFromRow(DataRow row);
}
Then you declare Employee or any other data classes like this
public class Employee : IDataObject
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Salary { get; set; }
public void FillFromRow(DataRow row)
{
ID = (int)row["ID"];
Name = (string)row["Name"];
Salary = (decimal)row["Salary"];
}
}
Now you can use generics again
public static IList<T> GetItems<T>(DataTable table)
where T : IDataObject, new()
{
var items = new List<T>();
if (table != null) {
foreach (DataRow row in table.Rows) {
T item = new T();
item.FillFromRow(row);
items.Add(item);
}
}
return items;
}

How to read value form generic method with List<T> as parameter

Maybe a silly question, I can read all the properties from list parameter but not the value in the fields of <T>.
This is the structure
public class TestRecord {
public string StringTest { get; set; }
public int IntegerTest { get; set; }
public DateTime DateTimeTest { get; set; }
}
The generic method
public void TestOfT<T>(List<T> pList) where T:class, new() {
T xt = (T)Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
// System.Reflection.PropertyInfo pi = xt.GetType().GetProperty("StringTest");
// object s = pi.GetValue(tp, null) ; -- failed
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
}
}
Test code for generic method
public void TestTCode() {
List<TestRecord> rec = new List<TestRecord>();
rec.Add(new TestRecord() {
StringTest = "string",
IntegerTest = 1,
DateTimeTest = DateTime.Now
});
TestOfT<TestRecord>(rec);
}
Thanks for your help.
the problem is you are reading the value from the new instance (which can be written simply as var xt = new T();.
if you want to get the property of the item you need to pull the value from the instance.
void TestOfT<T>(IEnumerable<T> list) where T: class, new()
{
var properties = typeof(T).GetProperties();
foreach (var item in list)
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(item, null);
Debug.WriteLine("{0} is {1}", name, value);
}
}
public void TestOfT<T>(List<T> pList) where T:class, new() {
var xt = Activator.CreateInstance(typeof(T));
foreach (var tp in pList[0].GetType().GetProperties()) {
Debug.WriteLine(tp.Name);
Debug.WriteLine(tp.PropertyType);
Debug.WriteLine(tp.GetType().Name);
Debug.WriteLine(tp.GetValue(pList[0], null));
}
}

How do you convert a DataTable into a generic list?

Currently, I'm using:
DataTable dt = CreateDataTableInSomeWay();
List<DataRow> list = new List<DataRow>();
foreach (DataRow dr in dt.Rows)
{
list.Add(dr);
}
Is there a better/magic way?
If you're using .NET 3.5, you can use DataTableExtensions.AsEnumerable (an extension method) and then if you really need a List<DataRow> instead of just IEnumerable<DataRow> you can call Enumerable.ToList:
IEnumerable<DataRow> sequence = dt.AsEnumerable();
or
using System.Linq;
...
List<DataRow> list = dt.AsEnumerable().ToList();
List<Employee> emp = new List<Employee>();
//Maintaining DataTable on ViewState
//For Demo only
DataTable dt = ViewState["CurrentEmp"] as DataTable;
//read data from DataTable
//using lamdaexpression
emp = (from DataRow row in dt.Rows
select new Employee
{
_FirstName = row["FirstName"].ToString(),
_LastName = row["Last_Name"].ToString()
}).ToList();
With C# 3.0 and System.Data.DataSetExtensions.dll,
List<DataRow> rows = table.Rows.Cast<DataRow>().ToList();
You could use
List<DataRow> list = new List<DataRow>(dt.Select());
dt.Select() will return all rows in your table, as an array of datarows, and the List constructor accepts that array of objects as an argument to initially fill your list with.
If you just want a list of values from the "ID" int field returned, you could use...
List<int> ids = (from row in dt.AsEnumerable() select Convert.ToInt32(row["ID"])).ToList();
You can create a extension function as :
public static List<T> ToListof<T>(this DataTable dt)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var columnNames = dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToList();
var objectProperties = typeof(T).GetProperties(flags);
var targetList = dt.AsEnumerable().Select(dataRow =>
{
var instanceOfT = Activator.CreateInstance<T>();
foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
{
properties.SetValue(instanceOfT, dataRow[properties.Name], null);
}
return instanceOfT;
}).ToList();
return targetList;
}
var output = yourDataInstance.ToListof<targetModelType>();
I have added some modification to the code from this answer (https://stackoverflow.com/a/24588210/4489664) because for nullable Types it will return exception
public static List<T> DataTableToList<T>(this DataTable table) where T: new()
{
List<T> list = new List<T>();
var typeProperties = typeof(T).GetProperties().Select(propertyInfo => new
{
PropertyInfo = propertyInfo,
Type = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType
}).ToList();
foreach (var row in table.Rows.Cast<DataRow>())
{
T obj = new T();
foreach (var typeProperty in typeProperties)
{
object value = row[typeProperty.PropertyInfo.Name];
object safeValue = value == null || DBNull.Value.Equals(value)
? null
: Convert.ChangeType(value, typeProperty.Type);
typeProperty.PropertyInfo.SetValue(obj, safeValue, null);
}
list.Add(obj);
}
return list;
}
using System.Data;
var myEnumerable = myDataTable.AsEnumerable();
List<MyClass> myClassList =
(from item in myEnumerable
select new MyClass{
MyClassProperty1 = item.Field<string>("DataTableColumnName1"),
MyClassProperty2 = item.Field<string>("DataTableColumnName2")
}).ToList();
Again, using 3.5 you may do it like:
dt.Select().ToList()
BRGDS
// this is better suited for expensive object creation/initialization
IEnumerable<Employee> ParseEmployeeTable(DataTable dtEmployees)
{
var employees = new ConcurrentBag<Employee>();
Parallel.ForEach(dtEmployees.AsEnumerable(), (dr) =>
{
employees.Add(new Employee()
{
_FirstName = dr["FirstName"].ToString(),
_LastName = dr["Last_Name"].ToString()
});
});
return employees;
}
A more 'magic' way, and doesn't need .NET 3.5.
If, for example, DBDatatable was returning a single column of Guids (uniqueidentifier in SQL) then you could use:
Dim gList As New List(Of Guid)
gList.AddRange(DirectCast(DBDataTable.Select(), IEnumerable(Of Guid)))
DataTable dt; // datatable should contains datacolumns with Id,Name
List<Employee> employeeList=new List<Employee>(); // Employee should contain EmployeeId, EmployeeName as properties
foreach (DataRow dr in dt.Rows)
{
employeeList.Add(new Employee{EmployeeId=dr.Id,EmplooyeeName=dr.Name});
}
The Easiest way of Converting the DataTable into the Generic list of class
using Newtonsoft.Json;
var json = JsonConvert.SerializeObject(dataTable);
var model = JsonConvert.DeserializeObject<List<ClassName>>(json);
DataTable.Select() doesnt give the Rows in the order they were present in the datatable.
If order is important I feel iterating over the datarow collection and forming a List is the right way to go or you could also use overload of DataTable.Select(string filterexpression, string sort).
But this overload may not handle all the ordering (like order by case ...) that SQL provides.
/* This is a generic method that will convert any type of DataTable to a List
*
*
* Example : List< Student > studentDetails = new List< Student >();
* studentDetails = ConvertDataTable< Student >(dt);
*
* Warning : In this case the DataTable column's name and class property name
* should be the same otherwise this function will not work properly
*/
The following are the two functions in which if we pass a
DataTable
and a user defined class.
It will then return the List of that class with the DataTable data.
public static List<T> ConvertDataTable<T>(DataTable dt)
{
List<T> data = new List<T>();
foreach (DataRow row in dt.Rows)
{
T item = GetItem<T>(row);
data.Add(item);
}
return data;
}
private static T GetItem<T>(DataRow dr)
{
Type temp = typeof(T);
T obj = Activator.CreateInstance<T>();
foreach (DataColumn column in dr.Table.Columns)
{
foreach (PropertyInfo pro in temp.GetProperties())
{
//in case you have a enum/GUID datatype in your model
//We will check field's dataType, and convert the value in it.
if (pro.Name == column.ColumnName){
try
{
var convertedValue = GetValueByDataType(pro.PropertyType, dr[column.ColumnName]);
pro.SetValue(obj, convertedValue, null);
}
catch (Exception e)
{
//ex handle code
throw;
}
//pro.SetValue(obj, dr[column.ColumnName], null);
}
else
continue;
}
}
return obj;
}
This method will check the datatype of field, and convert dataTable value in to that datatype.
private static object GetValueByDataType(Type propertyType, object o)
{
if (o.ToString() == "null")
{
return null;
}
if (propertyType == (typeof(Guid)) || propertyType == typeof(Guid?))
{
return Guid.Parse(o.ToString());
}
else if (propertyType == typeof(int) || propertyType.IsEnum)
{
return Convert.ToInt32(o);
}
else if (propertyType == typeof(decimal) )
{
return Convert.ToDecimal(o);
}
else if (propertyType == typeof(long))
{
return Convert.ToInt64(o);
}
else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
{
return Convert.ToBoolean(o);
}
else if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?))
{
return Convert.ToDateTime(o);
}
return o.ToString();
}
To call the preceding method, use the following syntax:
List< Student > studentDetails = new List< Student >();
studentDetails = ConvertDataTable< Student >(dt);
Change the Student class name and dt value based on your requirements. In this case the DataTable column's name and class property name should be the same otherwise this function will not work properly.
lPerson = dt.AsEnumerable().Select(s => new Person()
{
Name = s.Field<string>("Name"),
SurName = s.Field<string>("SurName"),
Age = s.Field<int>("Age"),
InsertDate = s.Field<DateTime>("InsertDate")
}).ToList();
Link to working DotNetFiddle Example
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Data.DataSetExtensions;
public static void Main()
{
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("SurName", typeof(string));
dt.Columns.Add("Age", typeof(int));
dt.Columns.Add("InsertDate", typeof(DateTime));
var row1= dt.NewRow();
row1["Name"] = "Adam";
row1["SurName"] = "Adam";
row1["Age"] = 20;
row1["InsertDate"] = new DateTime(2020, 1, 1);
dt.Rows.Add(row1);
var row2 = dt.NewRow();
row2["Name"] = "John";
row2["SurName"] = "Smith";
row2["Age"] = 25;
row2["InsertDate"] = new DateTime(2020, 3, 12);
dt.Rows.Add(row2);
var row3 = dt.NewRow();
row3["Name"] = "Jack";
row3["SurName"] = "Strong";
row3["Age"] = 32;
row3["InsertDate"] = new DateTime(2020, 5, 20);
dt.Rows.Add(row3);
List<Person> lPerson = new List<Person>();
lPerson = dt.AsEnumerable().Select(s => new Person()
{
Name = s.Field<string>("Name"),
SurName = s.Field<string>("SurName"),
Age = s.Field<int>("Age"),
InsertDate = s.Field<DateTime>("InsertDate")
}).ToList();
foreach(Person pers in lPerson)
{
Console.WriteLine("{0} {1} {2} {3}", pers.Name, pers.SurName, pers.Age, pers.InsertDate);
}
}
public class Person
{
public string Name { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
public DateTime InsertDate { get; set; }
}
}
Use System.Data namespace then you will get .AsEnumerable().
This worked for me:
Need at least .Net Framework 3.5,
Code below displays DataRow turned to Generic.IEnumerable, comboBox1 has been used for a better illustration.
using System.Linq;
DataTable dt = new DataTable();
dt = myClass.myMethod();
List<object> list = (from row in dt.AsEnumerable() select (row["name"])).ToList();
comboBox1.DataSource = list;
Output
public class ModelUser
{
#region Model
private string _username;
private string _userpassword;
private string _useremail;
private int _userid;
/// <summary>
///
/// </summary>
public int userid
{
set { _userid = value; }
get { return _userid; }
}
/// <summary>
///
/// </summary>
public string username
{
set { _username = value; }
get { return _username; }
}
/// <summary>
///
/// </summary>
public string useremail
{
set { _useremail = value; }
get { return _useremail; }
}
/// <summary>
///
/// </summary>
public string userpassword
{
set { _userpassword = value; }
get { return _userpassword; }
}
#endregion Model
}
public List<ModelUser> DataTableToList(DataTable dt)
{
List<ModelUser> modelList = new List<ModelUser>();
int rowsCount = dt.Rows.Count;
if (rowsCount > 0)
{
ModelUser model;
for (int n = 0; n < rowsCount; n++)
{
model = new ModelUser();
model.userid = (int)dt.Rows[n]["userid"];
model.username = dt.Rows[n]["username"].ToString();
model.useremail = dt.Rows[n]["useremail"].ToString();
model.userpassword = dt.Rows[n]["userpassword"].ToString();
modelList.Add(model);
}
}
return modelList;
}
static DataTable GetTable()
{
// Here we create a DataTable with four columns.
DataTable table = new DataTable();
table.Columns.Add("userid", typeof(int));
table.Columns.Add("username", typeof(string));
table.Columns.Add("useremail", typeof(string));
table.Columns.Add("userpassword", typeof(string));
// Here we add five DataRows.
table.Rows.Add(25, "Jame", "Jame#hotmail.com", DateTime.Now.ToString());
table.Rows.Add(50, "luci", "luci#hotmail.com", DateTime.Now.ToString());
table.Rows.Add(10, "Andrey", "Andrey#hotmail.com", DateTime.Now.ToString());
table.Rows.Add(21, "Michael", "Michael#hotmail.com", DateTime.Now.ToString());
table.Rows.Add(100, "Steven", "Steven#hotmail.com", DateTime.Now.ToString());
return table;
}
protected void Page_Load(object sender, EventArgs e)
{
List<ModelUser> userList = new List<ModelUser>();
DataTable dt = GetTable();
userList = DataTableToList(dt);
gv.DataSource = userList;
gv.DataBind();
}[enter image description here][1]
</asp:GridView>
</div>
We can use a Generic Method for converting DataTable to List instead of manually converting a DataTable to List.
Note: DataTable's ColumnName and Type's PropertyName should be same.
Call the below Method:
long result = Utilities.ConvertTo<Student>(dt ,out listStudent);
// Generic Method
public class Utilities
{
public static long ConvertTo<T>(DataTable table, out List<T> entity)
{
long returnCode = -1;
entity = null;
if (table == null)
{
return -1;
}
try
{
entity = ConvertTo<T>(table.Rows);
returnCode = 0;
}
catch (Exception ex)
{
returnCode = 1000;
}
return returnCode;
}
static List<T> ConvertTo<T>(DataRowCollection rows)
{
List<T> list = null;
if (rows != null)
{
list = new List<T>();
foreach (DataRow row in rows)
{
T item = CreateItem<T>(row);
list.Add(item);
}
}
return list;
}
static T CreateItem<T>(DataRow row)
{
string str = string.Empty;
string strObj = string.Empty;
T obj = default(T);
if (row != null)
{
obj = Activator.CreateInstance<T>();
strObj = obj.ToString();
NameValueCollection objDictionary = new NameValueCollection();
foreach (DataColumn column in row.Table.Columns)
{
PropertyInfo prop = obj.GetType().GetProperty(column.ColumnName);
if (prop != null)
{
str = column.ColumnName;
try
{
objDictionary.Add(str, row[str].ToString());
object value = row[column.ColumnName];
Type vType = obj.GetType();
if (value == DBNull.Value)
{
if (vType == typeof(int) || vType == typeof(Int16)
|| vType == typeof(Int32)
|| vType == typeof(Int64)
|| vType == typeof(decimal)
|| vType == typeof(float)
|| vType == typeof(double))
{
value = 0;
}
else if (vType == typeof(bool))
{
value = false;
}
else if (vType == typeof(DateTime))
{
value = DateTime.MaxValue;
}
else
{
value = null;
}
prop.SetValue(obj, value, null);
}
else
{
prop.SetValue(obj, value, null);
}
}
catch(Exception ex)
{
}
}
}
PropertyInfo ActionProp = obj.GetType().GetProperty("ActionTemplateValue");
if (ActionProp != null)
{
object ActionValue = objDictionary;
ActionProp.SetValue(obj, ActionValue, null);
}
}
return obj;
}
}
You can use a generic method like that for datatable to generic list
public static List<T> DataTableToList<T>(this DataTable table) where T : class, new()
{
try
{
List<T> list = new List<T>();
foreach (var row in table.AsEnumerable())
{
T obj = new T();
foreach (var prop in obj.GetType().GetProperties())
{
try
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
if (propertyInfo.PropertyType.IsEnum)
{
propertyInfo.SetValue(obj, Enum.Parse(propertyInfo.PropertyType, row[prop.Name].ToString()));
}
else
{
propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
}
}
catch
{
continue;
}
}
list.Add(obj);
}
return list;
}
catch
{
return null;
}
}
Converting DataTable to Generic Dictionary
public static Dictionary<object,IList<dynamic>> DataTable2Dictionary(DataTable dt)
{
Dictionary<object, IList<dynamic>> dict = new Dictionary<dynamic, IList<dynamic>>();
foreach(DataColumn column in dt.Columns)
{
IList<dynamic> ts = dt.AsEnumerable()
.Select(r => r.Field<dynamic>(column.ToString()))
.ToList();
dict.Add(column, ts);
}
return dict;
}
Use Extension :
public static class Extensions
{
#region Convert Datatable To List
public static IList<T> ToList<T>(this DataTable table) where T : new()
{
IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
IList<T> result = new List<T>();
foreach (var row in table.Rows)
{
var item = CreateItemFromRow<T>((DataRow)row, properties);
result.Add(item);
}
return result;
}
private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
{
T item = new T();
foreach (var property in properties)
{
property.SetValue(item, row[property.Name], null);
}
return item;
}
#endregion
}
To assign the DataTable rows to the generic List of class
List<Candidate> temp = new List<Candidate>();//List that holds the Candidate Class,
//Note:The Candidate class contains RollNo,Name and Department
//tb is DataTable
temp = (from DataRow dr in tb.Rows
select new Candidate()
{
RollNO = Convert.ToInt32(dr["RollNO"]),
Name = dr["Name"].ToString(),
Department = dr["Department"].ToString(),
}).ToList();
you can use following two Generic functions
private static List<T> ConvertDataTable<T>(DataTable dt)
{
List<T> data = new List<T>();
foreach (DataRow row in dt.Rows)
{
T item = GetItem<T>(row);
data.Add(item);
}
return data;
}
private static T GetItem<T>(DataRow dr)
{
Type temp = typeof(T);
T obj = Activator.CreateInstance<T>();
foreach (DataColumn column in dr.Table.Columns)
{
foreach (PropertyInfo pro in temp.GetProperties())
{
if (pro.Name == column.ColumnName)
pro.SetValue(obj, dr[column.ColumnName].ToString(), null);
else
continue;
}
}
return obj;
}
and use it as following
List<StudentScanExamsDTO> studentDetails = ConvertDataTable<StudentScanExamsDTO>(dt);
If anyone want's to create custom function to convert datatable to list
class Program
{
static void Main(string[] args)
{
DataTable table = GetDataTable();
var sw = new Stopwatch();
sw.Start();
LinqMethod(table);
sw.Stop();
Console.WriteLine("Elapsed time for Linq Method={0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
ForEachMethod(table);
sw.Stop();
Console.WriteLine("Elapsed time for Foreach method={0}", sw.ElapsedMilliseconds);
Console.ReadKey();
}
private static DataTable GetDataTable()
{
var table = new DataTable();
table.Columns.Add("ID", typeof(double));
table.Columns.Add("CategoryName", typeof(string));
table.Columns.Add("Active", typeof(double));
var rand = new Random();
for (int i = 0; i < 100000; i++)
{
table.Rows.Add(i, "name" + i, rand.Next(0, 2));
}
return table;
}
private static void LinqMethod(DataTable table)
{
var list = table.AsEnumerable()
.Skip(1)
.Select(dr =>
new Category
{
Id = Convert.ToInt32(dr.Field<double>("ID")),
CategoryName = dr.Field<string>("CategoryName"),
IsActive =
dr.Field<double>("Active") == 1 ? true : false
}).ToList();
}
private static void ForEachMethod(DataTable table)
{
var categoryList = new List<Category>(table.Rows.Count);
foreach (DataRow row in table.Rows)
{
var values = row.ItemArray;
var category = new Category()
{
Id = Convert.ToInt32(values[0]),
CategoryName = Convert.ToString(values[1]),
IsActive = (double)values[2] == 1 ? true : false
};
categoryList.Add(category);
}
}
private class Category
{
public int Id { get; set; }
public string CategoryName { get; set; }
public bool IsActive { get; set; }
}
}
If we execute above code, Foreach method finishes in 56ms while linq one takes 101ms ( for 1000 records).
So Foreach method is better to use.
Source:Ways to Convert Datatable to List in C# (with performance test example)
try this using Newtonsoft Json:
var json = JsonConvert.SerializeObject(dataTable);
var YourConvertedDataType = JsonConvert.DeserializeObject<YourDataType>(json);
To get List of values instead of ItemArray, do this:
List<string> s = dt.AsEnumerable().Select(x => x[0].ToString()).ToList();
The above assumes that you want list of string values from column 0.

Categories