Currently I am using this approach to convert datatables to POCO (Plain Old CLR Object).
public static List<T> Mapper<T>(DataTable dt) {
List<T> list = new();
T obj = default;
foreach (DataRow item in dt.Rows) {
obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
if (item.Table.Columns.Contains(prop.Name)) {
prop.SetValue(
obj,
(item[prop.Name] != DBNull.Value ? Convert.ChangeType(item[prop.Name], prop.PropertyType) : null),
null
);
}
else {
prop.SetValue(
obj,
null,
null
);
}
}
list.Add(obj);
}
return list;
}
Works as intended but the problem is performance. Is there any faster alternatives to convert datatable to POCO? Or is there a way to improve the code above to be faster? I can't use dapper for a specific reason.
CodePudding user response:
Well, you could even improve your reflection approach. You are doing some things multiple times for no reason like obj.GetType().GetProperties()
. So maybe this improves it already noticeably:
public static List<T> Mapper<T>(DataTable dt)
{
List<T> list = new();
Dictionary<PropertyInfo, DataColumn> propColumns = typeof(T).GetProperties()
.Where(p => dt.Columns.Contains(p.Name))
.ToDictionary(p => p, p => dt.Columns[p.Name]);
foreach (DataRow row in dt.Rows)
{
T obj = Activator.CreateInstance<T>();
foreach (var propCol in propColumns)
{
object value = row[propCol.Value];
propCol.Key.SetValue(
obj,
value != DBNull.Value ? Convert.ChangeType(value, propCol.Key.PropertyType) : null,
null
);
}
list.Add(obj);
}
return list;
}
This also uses a dictionary that is initialized once at the beginning for the mapping of the properties and columns(which exist).
CodePudding user response:
Here's what I came up with. It's similar to the other answer but I'd already written it so I figured I'd post it, as it does add a little something:
public static List<T> Map<T>(DataTable table) where T : new()
{
var list = new List<T>();
var properties = typeof(T).GetProperties();
var propertiesByColumn = new Dictionary<DataColumn, PropertyInfo>();
foreach (DataColumn column in table.Columns)
{
var property = properties.FirstOrDefault(pi => pi.Name == column.ColumnName);
if (property != null)
{
propertiesByColumn.Add(column, property);
}
}
foreach (var row in table.AsEnumerable())
{
var item = new T();
foreach (var column in propertiesByColumn.Keys)
{
propertiesByColumn[column].SetValue(item,
row.IsNull(column)
? null
: row[column]);
}
list.Add(item);
}
return list;
}
I also improved the name. Method names should be verb-based, not noun-based.
CodePudding user response:
How about this way:
static List<T> Mapper<T>(DataTable dt)
{
var json = JsonConvert.SerializeObject(dt);
return JsonConvert.DeserializeObject<List<T>>(json);
}
POCO:
class User
{
public int Id { get; set; }
public string Name { get; set; }
}
Test:
var dt = new DataTable();
dt.Columns.Add("id", typeof(int));
dt.Columns.Add("name", typeof(string));
dt.Rows.Add(1, "A");
dt.Rows.Add(2, "B");
dt.Rows.Add(3, "C");
var users = Mapper<User>(dt);
foreach (var user in users)
Console.WriteLine(user.Id " " user.Name);