I am trying to iterate through the generic type object list, i am able to get the properties of the object however unable to get the values from properties of each instance of the object. Here's how my code looks like: I want create a function that will convert any list passed to it and convert it into DataTable.
--DataObject
public class StudentDo
{
public int Id {get;set}
public string Name {get;set}
}
--Generic Data Access Object
public DataTable ConvertListToDataTable(List<T> list, string tableName = "")
{
var type = typeof(T);
var properties = type.GetProperties().ToList();
DataTable dt = new DataTable(tableName);
properties.ForEach(x =>
{
dt.Columns.Add(x.Name);
});
// i don't know how shall i pull data from each instance of List<T>.
return dt;
}
Iterate over the list and insert against each column using reflection -
public static DataTable ConvertListToDataTable<T>(List<T> list, string tableName = "")
{
var type = typeof(T);
var properties = type.GetProperties().ToList();
DataTable dt = new DataTable(tableName);
properties.ForEach(x =>
{
dt.Columns.Add(x.Name);
});
foreach (var item in list)
{
var dataRow = dt.NewRow();
properties.ForEach(x =>
{
dataRow[x.Name] = x.GetValue(item, null);
});
dt.Rows.Add(dataRow);
}
return dt;
}
This is what I use:
public DataTable ToDataTable<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;
}
I want to make it a little easier, to assign values of the DataRow Collection to Properties of some Objects.
I want to do something like this:
Tabelle t = new Tabelle(query, Id, 1200);
foreach (DataRow r in t.Rows)
{
CustObject b = new CustObject();
r.AssingValue(Columnname, b, PropertyName);
}
The Table looks like this:
internal class Tabelle : DataTable
{
DataTable tabelle = new DataTable();
internal Tabelle(string abfrage)
{
try
{
SqlDataAdapter adapter = new SqlDataAdapter((SqlCommand)new Prozedur(abfrage));
if (adapter.SelectCommand.Connection != null && adapter.SelectCommand.Connection.State == ConnectionState.Open)
{
adapter.Fill(this);
}
}
}
// How can I attach this method to the datarow Collection?
public void AssingValue(string parametername, object target, string propertyName)
{
if (Row.Value != System.DBNull.Value) // how would I access the DataRow Value?
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, Convert.ToDateTime(Row.Value));
}
}
}
if i understand you correctly, you can achieve this with an extention method on the datarow class itself
public static class DataRowExtensionMethods
{
public static int ReturnIntForField(this DataRow data, string fieldName)
{
return data[fieldName] == DBNull.Value ? 0 : Convert.ToInt32(data[fieldName]);
}
public static DateTime ReturnDateTimeFromDataRowField(this DataRow data, string fieldName)
{
return Convert.ToDateTime(data[fieldName]);
}
} \\etc... etc...
We can then re-write your datarow loop as per the below.
foreach (DataRow r in t.Rows)
{
CustObject b = new CustObject();
b.PropertyName = r.ReturnIntForField("ColumnName");
b.PropertyName1 = r.ReturnDateTimeFromDataRowField("ColumnName1");
}
Hope that helps
your Assign method should have input parameters such as rowIndex to find specific row in your table and columnName, to get/set row's column value.
You can try something like this:
public void AssingValue(string columnName, object target, string propertyName, int rowIndex)
{
DataRow row = this.Rows[rowIndex];
if (row[columnName] != System.DBNull.Value) // how would I access the DataRow Value?
{
var prop = target.GetType().GetProperty(propertyName);
prop.SetValue(target, Convert.ToDateTime(row[columnName]));
}
}
After some Google i found following code that can help to convert data row to specifics class
public static void SetItemFromRow(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
p.SetValue(item, row[c], null);
}
}
}
// function that creates an object from the given data row
public static T CreateItemFromRow(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static List CreateListFromTable(DataTable tbl) where T : new()
{
// define return list
List lst = new List();
// go through each row
foreach (DataRow r in tbl.Rows)
{
// add to the list
lst.Add(CreateItemFromRow(r));
}
// return the list
return lst;
}
But both of them are giving me this error "Constraints are not allowed on non-generic declaration" I have Google a lot but no help regarding my function
The main thing i want is to make genric function that convert data table to list of class
& data row to class
You forgot the <T>
public static void SetItemFromRow<T>(T item, DataRow row)
where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
p.SetValue(item, row[c], null);
}
}
}
I created a DataRow on my project:
DataRow datarow;
I want to convert this DataRow to any Type of Object.
How could I do it?
This is a pretty cool way I use it.
public static T ToObject<T>(this DataRow dataRow)
where T : new()
{
T item = new T();
foreach (DataColumn column in dataRow.Table.Columns)
{
PropertyInfo property = GetProperty(typeof(T), column.ColumnName);
if (property != null && dataRow[column] != DBNull.Value && dataRow[column].ToString() != "NULL")
{
property.SetValue(item, ChangeType(dataRow[column], property.PropertyType), null);
}
}
return item;
}
private static PropertyInfo GetProperty(Type type, string attributeName)
{
PropertyInfo property = type.GetProperty(attributeName);
if (property != null)
{
return property;
}
return type.GetProperties()
.Where(p => p.IsDefined(typeof(DisplayAttribute), false) && p.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().Single().Name == attributeName)
.FirstOrDefault();
}
public static object ChangeType(object value, Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
return Convert.ChangeType(value, Nullable.GetUnderlyingType(type));
}
return Convert.ChangeType(value, type);
}
I Have found one solution for my application.
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
p.SetValue(item, row[c], null);
}
}
}
This will map your DataRow to ViewModel, Like below.
Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row);
class Person{
public string FirstName{get;set;}
public string LastName{get;set;}
}
Person person = new Person();
person.FirstName = dataRow["FirstName"] ;
person.LastName = dataRow["LastName"] ;
or
Person person = new Person();
person.FirstName = dataRow.Field<string>("FirstName");
person.LastName = dataRow.Field<string>("LastName");
Similar to some of the previous approaches, I created this extension method for DataRow which takes an argument object to be populated. Main difference is that in addition to populating object's Properties, it also populates Fields of given object. This should also work for simpler structures (Though I only tested on objects).
public static T ToObject<T>( this DataRow dataRow )
where T : new() {
T item = new T();
foreach( DataColumn column in dataRow.Table.Columns ) {
if( dataRow[column] != DBNull.Value ) {
PropertyInfo prop = item.GetType().GetProperty( column.ColumnName );
if( prop != null ) {
object result = Convert.ChangeType( dataRow[column], prop.PropertyType );
prop.SetValue( item, result, null );
continue;
}
else {
FieldInfo fld = item.GetType().GetField( column.ColumnName );
if( fld != null ) {
object result = Convert.ChangeType( dataRow[column], fld.FieldType );
fld.SetValue( item, result );
}
}
}
}
return item;
}
You can put this code in your current class or in a global static class.
It needs following namespaces...
using System;
using System.Data;
using System.Reflection;
Usage is as simple as...
MyClassName obj = dataRow.ToObject<MyClassName>()
Here is an extension method that would allow you to convert a DataRow to a given object.
public static class DataRowExtensions
{
public static T Cast<T>(this DataRow dataRow) where T : new()
{
T item = new T();
IEnumerable<PropertyInfo> properties = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.CanWrite);
foreach (DataColumn column in dataRow.Table.Columns)
{
if (dataRow[column] == DBNull.Value)
{
continue;
}
PropertyInfo property = properties.FirstOrDefault(x => column.ColumnName.Equals(x.Name, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
continue;
}
try
{
Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeValue = (dataRow[column] == null) ? null : Convert.ChangeType(dataRow[column], t);
property.SetValue(item, safeValue, null);
}
catch
{
throw new Exception($"The value '{dataRow[column]}' cannot be mapped to the property '{property.Name}'!");
}
}
return item;
}
}
And you can use the above extension method like so
foreach (DataRow row in dataTable.Rows)
{
SomeClassType obj = row.Cast<SomeClassType>();
// do something with your object
}
Given Converter<TIn, TOut> is a delegate, then the following should work:
List<Person> personList = new List<Person>();
personList = ConvertDataRowToList(ds, (row) => {
return new Person
{
FirstName = row["FirstName"],
LastName = row["LastName"]
// Rest of properties should assign here...
};
});
https://learn.microsoft.com/en-us/dotnet/api/system.converter-2
Apart from the manual method Avi shows, you can use a mapping system like AutoMapper to do the transformation for you. This is particularly useful in the case where you have a lot of columns/properties to map.
Check out this article on how to use AutoMapper to convert a DataTable to a list of objects.
DataRow has a property ItemArray, which contains an array of object values. You can work with this array and create any custom type with the values from your DataRow.
With less complications ;), two steps will solve the task:
1. cast to dictionary (ToDictionary).
2. map dictionary to entity (MapToEntity).
public static IDictionary<string, object> ToDictionary(
this DataRow content
)
{
var values = content.ItemArray;
var columns = content
.Table
.Columns
.Cast<DataColumn>()
.Select(x => x.ColumnName);
return values
.Select((v, m) => new { v, m })
.ToDictionary(
x => columns.ElementAt(x.m)
, x => (x.v == DBNull.Value ? null : x.v)
);
}
public static T MapToEntity<T>(
this IDictionary<string, object> source
)
where T : class, new()
{
// t - target
T t_object = new T();
Type t_type = t_object.GetType();
foreach (var kvp in source)
{
PropertyInfo t_property = t_type.GetProperty(kvp.Key);
if (t_property != null)
{
t_property.SetValue(t_object, kvp.Value);
}
}
return t_object;
}
...and the usage would be:
DataRow dr = getSomeDataRow(someArgs);
ABC result = dr.ToDictionary()
.MapToEntity<ABC>();
You could convert the whole Data table into a list Object like the code below. Of course, you can take the specific object which you want with the index or the field value.
/// <summary>
/// convert a datatable to list Object
/// </summary>
/// <typeparam name="T">object model</typeparam>
/// <param name="dataTable"></param>
/// <returns>ex ussage: List<User> listTbl = CommonFunc.convertDatatblToListObj<User>(dataTable);</returns>
public static List<T> convertDatatableToListObject<T>(DataTable dataTable)
{
List<T> res = new List<T>();
try
{
string tblJson = JsonConvert.SerializeObject(dataTable);
res = JsonConvert.DeserializeObject<List<T>>(tblJson);
}
catch (Exception ex)
{
string exStr = ex.Message;
}
return res;
}
With these changes worked fine for me, for fields int, long, int? and long?
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
if (p.PropertyType.Name == "Int64")
{
p.SetValue(item, long.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.Name == "Int32")
{
p.SetValue(item, int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int32"))
{
p.SetValue(item, (int?)int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int64"))
{
p.SetValue(item, (long?)long.Parse(row[c].ToString()), null);
}
else
{
p.SetValue(item, row[c], null);
}
}
}
}
I have a datatable like this
Name| Value
----|------
NA | VA
NB | VB
NC | VC1
NC | VC2
ND | VD1
ND | VD2
and a class like this
Class NVMapping {
List<string> NC { get; set; }
List<string> ND { get; set; }
string NA { get; set; }
string NB { get; set; }
}
How to use linq or other way to transfer the datatable to this type ?
I think I need to emphasize one thing here. This kinda mapping will be a lot in my application.
Somehow I think using reflection can make this function be generic to handle all these kinda mapping.
So if possible, I would prefer a generic function like using reflection to achieve this.
If possible, it will even better just transfering datatable into an object like above transformation.
Thanks !
May I suggest writing a generic method that uses reflection. The following method uses reflection to populate a class's public properties from a DataRow in a DataTable (or a List<> of classes, one from each DataRow in the DataTable) where the ColumnName matches the name of the public property in the class exactly (case-sensitive).
If the DataTable has extra columns that don't match up to a property in the class, they are ignored. If the DataTable is missing columns to match a class property, that property is ignored and left at the default value for that type (since it is a property).
public static IList<T> DatatableToClass<T>(DataTable Table) where T : class, new()
{
if (!Helper.IsValidDatatable(Table))
return new List<T>();
Type classType = typeof(T);
IList<PropertyInfo> propertyList = classType.GetProperties();
// Parameter class has no public properties.
if (propertyList.Count == 0)
return new List<T>();
List<string> columnNames = Table.Columns.Cast<DataColumn>().Select(column => column.ColumnName).ToList();
List<T> result = new List<T>();
try
{
foreach (DataRow row in Table.Rows)
{
T classObject = new T();
foreach (PropertyInfo property in propertyList)
{
if (property != null && property.CanWrite) // Make sure property isn't read only
{
if (columnNames.Contains(property.Name)) // If property is a column name
{
if (row[property.Name] != System.DBNull.Value) // Don't copy over DBNull
{
object propertyValue = System.Convert.ChangeType(
row[property.Name],
property.PropertyType
);
property.SetValue(classObject, propertyValue, null);
}
}
}
}
result.Add(classObject);
}
return result;
}
catch
{
return new List<T>();
}
}
If you interested in going the other way, and fill out a DataTable from a class's public properties, I cover that and more on my C# blog, CSharpProgramming.tips/Class-to-DataTable
Here it is:
IEnumerable<DataRow> rows = table.AsEnumerable();
string naValue = null;
var naRow = rows.FirstOrDefault(r => r.Field<string>("Name") == "NA");
if(naRow != null)
naValue = naRow.Field<string>("Value");
string nbValue = null;
var nbRow = rows.FirstOrDefault(r => r.Field<string>("Name") == "NB");
if(nbRow != null)
nbValue = nbRow.Field<string>("Value");
NVMapping map = new NVMapping {
NC = rows.Where(r => r.Field<string>("Name") == "NC")
.Select(r => r.Field<string>("Value")).ToList(),
ND = rows.Where(r => r.Field<string>("Name") == "ND")
.Select(r => r.Field<string>("Value")).ToList(),
NA = naValue,
NB = nbValue
};