Home > Mobile >  Faster way to convert DataTable to POCO
Faster way to convert DataTable to POCO

Time:09-02

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);
  • Related