Extend Custom Datatable to provide additional methods - c#

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

Related

Iterate through Generic Typed List in c#

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

Using GetField<T> to set values in an object from a DataRow

I'm trying to write a generic method to return values from columns in a DataRow.
protected static T GetField<T>(DataRow row, string name, T defaultValue)
{
if (row == null)
{
throw new ArgumentNullException("row");
}
T result = defaultValue;
if (row.Table.Columns.Contains(name) && !row.IsNull(name))
{
result = row.Field<T>(name);
}
return result;
}
When trying to assign values to specific tests I get 'Specified cast is not valid.' exception.
var rule = new MyObj
{
AString = GetField(row, "AnswerId", "test"),
AnInt = GetField(row, "Decline", 0),
ADecimal = GetField(row, "LoadFactor", 1M),
};
I'm trying to avoid writing an overload for each type.
Can this be done?
You can use these extension Methods to convert whole DataTable to List, instead of writing method for DataRow:
public static class DataTableExtensions
{
public static List<T> ToList<T>(this DataTable table) where T : new()
{
List<PropertyInfo> properties = typeof(T).GetProperties().ToList();
List<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, List<PropertyInfo> properties) where T : new()
{
T item = new T();
foreach (var property in properties)
{
if (row.Table.Columns.Contains(property.Name))
{
if (row[property.Name] != DBNull.Value)
property.SetValue(item, row[property.Name], null);
}
}
return item;
}
}
and use it like this:
List<SomeType> list = SomeDataTable.ToList<SomeType>();
But you have to make sure that name of columns that are selected in query should match the class properties and datatypes should also match.

How to Convert DataRow to an Object

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

Make Reachable Class Out Of DataTable LINQToDataTable<T>()?

Within my code I get instances of needing to convert a query list into table. I use the following method to achieve this:
//Attach query results to DataTables
public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).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));
}
}
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;
}
it works perfectly, in the following example:
DataTable gridTable = LINQToDataTable(GetGrids); // Loads Query into Table
Instead of duplicating the method in various .cs files - How would it look if it was a utility class of its own that would allow me to write something like the following:
DataTable gridTable = Utility.LINQToDataTable(GetGrids); // Loads Query into Table
so as to avoid numerous duplications ??
move your method to Utility calls and make it as static
public class Utility
{
public static DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
// code ....
}
}
now you can call it as :
DataTable gridTable = Utility.LINQToDataTable(GetGrids);
public static class EnumerableExtensions
{
public static DataTable ToDataTable<T>(this IEnumerable<T> varlist)
{
// .. existing code here ..
}
}
Use it as follows:
GetGrids.ToDataTable();
// just like the others
GetGrids.ToList();

Convert DataTable to Generic List in C#

Disclaimer: I know its asked at so many places at SO.
My query is a little different.
Coding Language: C# 3.5
I have a DataTable named cardsTable that pull data from DB and I have a class Cards which have only some properties(no constructor)
public class Cards
{
public Int64 CardID { get; set; }
public string CardName { get; set; }
public Int64 ProjectID { get; set; }
public Double CardWidth { get; set; }
public Double CardHeight { get; set; }
public string Orientation { get; set; }
public string BackgroundImage { get; set; }
public string Background { get; set; }
}
I want to insert the cardsTable data to an object of type List.
My data will be having null fields in it and so the method should not error when i convert the data. Is the below method the best way?
DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().ToList().ConvertAll(x => new Cards { CardID = (Int64)x.ItemArray[0] });
You could actually shorten it down considerably. You can think of the Select() extension method as a type converter. The conversion could then be written as this:
List<Cards> target = dt.AsEnumerable()
.Select(row => new Cards
{
// assuming column 0's type is Nullable<long>
CardID = row.Field<long?>(0).GetValueOrDefault(),
CardName = String.IsNullOrEmpty(row.Field<string>(1))
? "not found"
: row.Field<string>(1),
}).ToList();
I think all the solutions can be improved and make the method more general if you use some conventions and reflection. Let's say you name your columns in the datatable the same name as the properties in your object, then you could write something that look at all your properties of your object and then look up that column in the datatable to map the value.
I did the opposite, that is... from IList to datatable, and the code I wrote can be seen at: http://blog.tomasjansson.com/convert-datatable-to-generic-list-extension/
It shouldn't be that hard to go the other way, and it should be that hard to overload the functions so you can provide information of which properties you want to include or exclude.
EDIT:
So the code to make it work is:
public static class DataTableExtensions
{
private static Dictionary<Type,IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
public static IList<PropertyInfo> GetPropertiesForType<T>()
{
var type = typeof(T);
if(!typeDictionary.ContainsKey(typeof(T)))
{
typeDictionary.Add(type, type.GetProperties().ToList());
}
return typeDictionary[type];
}
public static IList<T> ToList<T>(this DataTable table) where T : new()
{
IList<PropertyInfo> properties = GetPropertiesForType<T>();
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;
}
}
If you have a DataTable you can just write yourTable.ToList<YourType>() and it will create the list for you. If you have more complex type with nested objects you need to update the code. One suggestion is to just overload the ToList method to accept an params string[] excludeProperties which contains all your properties that shouldn't be mapped. Of course you can add some null checking in the foreach loop of the CreateItemForRow method.
UPDATE: Added static dictionary to store the result from the reflection operation to make it a little bit faster. I haven't compiled the code, but it should work :).
Just a little simplification. I don't use ItemArray:
List<Person> list = tbl.AsEnumerable().Select(x => new Person
{
Id = (Int32) (x["Id"]),
Name = (string) (x["Name"] ?? ""),
LastName = (string) (x["LastName"] ?? "")
}).ToList();
The .ToList() is in the wrong place, and if some fields can be null you'll have to deal with these as they wont convert to Int64 if they're null
DataTable dt = GetDataFromDB();
List<Cards> target = dt.AsEnumerable().Select(
x => new Cards { CardID = (Int64)(x.ItemArray[0] ?? 0) }).ToList();
well its the one line solution
it depends on whether or not you know the data in the database is all valid and will not contain anything that will break the above
eg a nullable field whenre you dont expect it - maybe due to a left join int eh sql that genertates the data.
So if you have validated the data before then yeah - I was goign to suggest some linq - but you got tht down.
If you need some validation however you should probably just loop through the datarows, generate your object as above and add it to the collection ... this will also allow you to handle errors in one row and still process the rest.
Thats the way i see it anyway
(damn i came on to downvote something so my rep was 1024)
You can map Data Table to model class using a Generic class like below.
Generic class
public static class DataTableMappingtoModel
{
/// <summary>
/// Maps Data Table values to coresponded model propertise
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static List<T> MappingToEntity<T>(this DataTable dt)
{
try
{
var lst = new List<T>();
var tClass = typeof (T);
PropertyInfo[] proInModel = tClass.GetProperties();
List<DataColumn> proInDataColumns = dt.Columns.Cast<DataColumn>().ToList();
T cn;
foreach (DataRow item in dt.Rows)
{
cn = (T) Activator.CreateInstance(tClass);
foreach (var pc in proInModel)
{
var d = proInDataColumns.Find(c => string.Equals(c.ColumnName.ToLower().Trim(), pc.Name.ToLower().Trim(), StringComparison.CurrentCultureIgnoreCase));
if (d != null)
pc.SetValue(cn, item[pc.Name], null);
}
lst.Add(cn);
}
return lst;
}
catch (Exception e)
{
throw e;
}
}
}
Model class
public class Item
{
public string ItemCode { get; set; }
public string Cost { get; set; }
public override string ToString()
{
return "ItemCode : " + ItemCode + ", Cost : " + Cost;
}
}
Create DataTable
public DataTable getTable()
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("ItemCode", typeof(string)));
dt.Columns.Add(new DataColumn("Cost", typeof(string)));
DataRow dr;
for (int i = 0; i < 10; i++)
{
dr = dt.NewRow();
dr[0] = "ItemCode" + (i + 1);
dr[1] = "Cost" + (i + 1);
dt.Rows.Add(dr);
}
return dt;
}
Now we can convert this DataTable to List like below:
DataTable dt = getTable();
List<Item> lst = dt.ToCollection<Item>();
foreach (Item cn in lst)
{
Response.Write(cn.ToString() + "<BR/>");
}
Hope will help you
Here is a simple way to convert to generic list in c# with Where condition
List<Filter> filter = ds.Tables[0].AsEnumerable()
.Where(x => x.Field<int>("FilterID") == 5)
.Select(row => new Filter
{
FilterID = row.Field<int>("FilterID"),
FilterName = row.Field<string>("FilterName")
}).ToList();
First Define properties and use as per
public class Filter
{
public int FilterID { get; set; }
public string FilterName { get; set; }
}
Put Package:
using System.Linq;
using System.Collections.Generic;
Here is the fastest loop free solution to convert DataTable to a generic type list.
public static List<T> ConvertDataTable<T>(DataTable SourceData, Func<DataRow, T> RowConverter)
{
List<T> list = new List<T>();
if (SourceData == null || SourceData.Rows.Count < 1)
return list;
IEnumerable<T> enumerable = SourceData.AsEnumerable().Select(RowConverter);
if (enumerable == null)
return list;
return new List<T>(enumerable);
}
And this is the implementation of this function.
public static List<T> ExecuteListOfObject<T>(DataTable SourceData)
{
return ConvertDataTable(SourceData, ConvertRecord<T>);
}
public static T ConvertRecord<T>(DataRow drData)
{
if (drData == null || drData[0] == DBNull.Value)
return default(T);
return (T)drData[0];
}
I built on top of Tomas Jansson's logic to include an "Ignore" attribute. This allows me to add other attribute's to the class being loaded without breaking the DataTable-To-Class loading itself.
Alternatively I also considered adding a separate parameter that holds the actual column name to be read from in the DataTable. In that case instead of using "row[property.Name]" then you'd use row[attribute.Name]" or something like that for that particular property.
public static class DataTableExtensions
{
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public sealed class IgnoreAttribute : Attribute { public IgnoreAttribute() { } }
private static Dictionary<Type, IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
public static IList<PropertyInfo> GetPropertiesForType<T>()
{
var type = typeof(T);
if (!typeDictionary.ContainsKey(typeof(T)))
typeDictionary.Add(type, type.GetProperties().ToList());
return typeDictionary[type];
}
public static IList<T> ToList<T>(this DataTable table) where T : new()
{
IList<PropertyInfo> properties = GetPropertiesForType<T>();
IList<T> result = new List<T>();
foreach (var row in table.Rows)
result.Add(CreateItemFromRow<T>((DataRow)row, properties));
return result;
}
private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
{
T item = new T();
foreach (var property in properties)
{
// Only load those attributes NOT tagged with the Ignore Attribute
var atr = property.GetCustomAttribute(typeof(IgnoreAttribute));
if (atr == null)
property.SetValue(item, row[property.Name], null);
}
return item;
}
}
Coming late but this can be useful. Can be called using:
table.Map();
or call with a Func to filter the values.
You can even change the mapping name between the type property and DataColumn header by setting the attributes on the property.
[AttributeUsage(AttributeTargets.Property)]
public class SimppleMapperAttribute: Attribute
{
public string HeaderName { get; set; }
}
public static class SimpleMapper
{
#region properties
public static bool UseDeferredExecution { get; set; } = true;
#endregion
#region public_interface
public static IEnumerable<T> MapWhere<T>(this DataTable table, Func<T, bool> sortExpression) where T:new()
{
var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties())).Where((t)=>sortExpression(t));
return UseDeferredExecution ? result : result.ToArray();
}
public static IEnumerable<T> Map<T>(this DataTable table) where T : new()
{
var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties()));
return UseDeferredExecution ? result : result.ToArray();
}
#endregion
#region implementation_details
private static T ConvertRow<T>(DataRow row, DataColumnCollection columns, System.Reflection.PropertyInfo[] p_info) where T : new()
{
var instance = new T();
foreach (var info in p_info)
{
if (columns.Contains(GetMappingName(info))) SetProperty(row, instance, info);
}
return instance;
}
private static void SetProperty<T>(DataRow row, T instance, System.Reflection.PropertyInfo info) where T : new()
{
string mp_name = GetMappingName(info);
object value = row[mp_name];
info.SetValue(instance, value);
}
private static string GetMappingName(System.Reflection.PropertyInfo info)
{
SimppleMapperAttribute attribute = info.GetCustomAttributes(typeof(SimppleMapperAttribute),true).Select((o) => o as SimppleMapperAttribute).FirstOrDefault();
return attribute == null ? info.Name : attribute.HeaderName;
}
#endregion
}

Categories