Prihaan
Prihaan

Reputation: 15

Convert datatable object to list - code explanation?

I found the below code on Stack Overflow. But I am not getting what fundamentally this code is doing. Can anyone please explain how this code works?

public static List<T> ToListof<T>(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;
}

Specifically, I would like to know where the columns' data are being typecast.

Upvotes: 1

Views: 198

Answers (2)

Venson
Venson

Reputation: 1870

In Detail:

const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

this will combine the public and the instance flag so that only Public non static methods will be searched.

 var columnNames = dt.Columns.Cast<DataColumn>()
    .Select(c => c.ColumnName)
    .ToList();

this will list all column names from the data table

var objectProperties = typeof(T).GetProperties(flags);

gets the Type of the generic argument and will list all public, non static properties

dt.AsEnumerable().Select

creates an IEnumerable of each data row in the DataTable

var instanceOfT = Activator.CreateInstance<T>();

this creates a new instance as you would use new

 foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
 {
     properties.SetValue(instanceOfT, dataRow[properties.Name], null);
 }

this will iterate through all propertys of T whos also contained in the datatable and not null (eg. DbNull from the database)

then it calls SetValue. As the dataRow will already return the value as its stored in the database there is no cast nessesary. This does only work if the Property and the type in the database are "the same". As NVarchar for string.

Upvotes: 1

Avner Shahar-Kashtan
Avner Shahar-Kashtan

Reputation: 14700

It attempts to convert a datatable to a list of objects of type T, dynamically at runtime.

var objectProperties = typeof(T).GetProperties(flags);

This line uses Reflection to get a list of public properties on type T.

var targetList = dt.AsEnumerable().Select(dataRow =>

This line iterates the DataTable as an IEnumerable, getting an instance called dataRow for each row.

var instanceOfT = Activator.CreateInstance<T>();

This creates a new instance of type T using reflection, inside the loop. This means a new T is created for each dataRow.

foreach (var properties in objectProperties.Where(properties => 
                  columnNames.Contains(properties.Name) 

This goes over all the properties of T we got back in the beginning, which are also in columnNames - meaning that there's a column with value for them

  && dataRow[properties.Name] != DBNull.Value))

The second half of the condition makes sure that the column has a value and isn't NULL.

  properties.SetValue(instanceOfT, dataRow[properties.Name], null);

This uses reflection, again to set the value from the datarow into the property of T.

).ToList();

This takes all the items returned from the Select statement and returns a List from them.

The code isn't the neatest, but the variables are pretty well-named and clear, if you know how reflection works. As for your second question - there's no casting, because this code assumes that the type of the value in the DataRow matches the type of the property. If it doesn't, an exception will be thrown.

Upvotes: 1

Related Questions