1

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.

6
  • where coloumn's data is getting type casted <= It isn't, at least not in the code above. It is done by the SetValue method which takes an System.object as parameter and all types eventually inherit from System.object Commented Aug 1, 2018 at 13:28
  • At Cast<DataColumn> it is A system.linq built in method Commented Aug 1, 2018 at 13:28
  • 1
    If you want to know how something works and you are not sure why it works when reading it then debug it. Copy/paste it into a throwaway project and step through it at run time. Commented Aug 1, 2018 at 13:29
  • It probably gets type casted inside the SetValue method. Commented Aug 1, 2018 at 13:30
  • the cast of the cells is in the properties.SetValue(instanceOfT, dataRow[properties.Name], null); - dataRow[properties.Name] is the value from the cell, and SetValue puts it into the property Commented Aug 1, 2018 at 13:31

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.