Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I easily convert DataReader to List<T>? [duplicate]

People also ask

How do you handle invalid attempt to read when no data is present?

You have to call dr. Read() before attempting to read any data. That method will return false if there is nothing to read.


I would suggest writing an extension method for this:

public static IEnumerable<T> Select<T>(this IDataReader reader,
                                       Func<IDataReader, T> projection)
{
    while (reader.Read())
    {
        yield return projection(reader);
    }
}

You can then use LINQ's ToList() method to convert that into a List<T> if you want, like this:

using (IDataReader reader = ...)
{
    List<Customer> customers = reader.Select(r => new Customer {
        CustomerId = r["id"] is DBNull ? null : r["id"].ToString(),
        CustomerName = r["name"] is DBNull ? null : r["name"].ToString() 
    }).ToList();
}

I would actually suggest putting a FromDataReader method in Customer (or somewhere else):

public static Customer FromDataReader(IDataReader reader) { ... }

That would leave:

using (IDataReader reader = ...)
{
    List<Customer> customers = reader.Select<Customer>(Customer.FromDataReader)
                                     .ToList();
}

(I don't think type inference would work in this case, but I could be wrong...)


I have written the following method using this case.

First, add the namespace: System.Reflection

For Example: T is return type(ClassName) and dr is parameter to mapping DataReader

C#, Call mapping method like the following:

List<Person> personList = new List<Person>();
personList = DataReaderMapToList<Person>(dataReaderForPerson);

This is the mapping method:

public static List<T> DataReaderMapToList<T>(IDataReader dr)
{
    List<T> list = new List<T>();
    T obj = default(T);
    while (dr.Read()) {
        obj = Activator.CreateInstance<T>();
        foreach (PropertyInfo prop in obj.GetType().GetProperties()) {
            if (!object.Equals(dr[prop.Name], DBNull.Value)) {
                prop.SetValue(obj, dr[prop.Name], null);
            }
        }
        list.Add(obj);
    }
    return list;
}

VB.NET, Call mapping method like the following:

Dim personList As New List(Of Person)
personList = DataReaderMapToList(Of Person)(dataReaderForPerson)

This is the mapping method:

Public Shared Function DataReaderMapToList(Of T)(ByVal dr As IDataReader) As List(Of T)
        Dim list As New List(Of T)
        Dim obj As T
        While dr.Read()
            obj = Activator.CreateInstance(Of T)()
            For Each prop As PropertyInfo In obj.GetType().GetProperties()
                If Not Object.Equals(dr(prop.Name), DBNull.Value) Then
                    prop.SetValue(obj, dr(prop.Name), Nothing)
                End If
            Next
            list.Add(obj)
        End While
        Return list
    End Function

I have seen systems that use Reflection and attributes on Properties or fields to maps DataReaders to objects. (A bit like what LinqToSql does.) They save a bit of typing and may reduce the number of errors when coding for DBNull etc. Once you cache the generated code they can be faster then most hand written code as well, so do consider the “high road” if you are doing this a lot.

See "A Defense of Reflection in .NET" for one example of this.

You can then write code like

class CustomerDTO  
{
    [Field("id")]
    public int? CustomerId;

    [Field("name")]
    public string CustomerName;
}

...

using (DataReader reader = ...)
{    
   List<CustomerDTO> customers = reader.AutoMap<CustomerDTO>()
                                    .ToList();
}

(AutoMap(), is an extension method)


@Stilgar, thanks for a great comment

If are able to you are likely to be better of using NHibernate, EF or Linq to Sql, etc However on old project (or for other (sometimes valid) reasons, e.g. “not invented here”, “love of stored procs” etc) It is not always possible to use a ORM, so a lighter weight system can be useful to have “up your sleeves”

If you every needed too write lots of IDataReader loops, you will see the benefit of reducing the coding (and errors) without having to change the architecture of the system you are working on. That is not to say it’s a good architecture to start with..

I am assuming that CustomerDTO will not get out of the data access layer and composite objects etc will be built up by the data access layer using the DTO objects.


A few years after I wrote this answer Dapper entered the world of .NET, it is likely to be a very good starting point for writing your onw AutoMapper, maybe it will completely remove the need for you to do so.


The simplest Solution :

var dt = new DataTable();
dt.Load(myDataReader);
List<DataRow> rows = dt.AsEnumerable();
var customers = rows.Select(dr=>new Customer(...)).ToList();

I would (and have) started to use Dapper. To use your example would be like (written from memory):

public List<CustomerEntity> GetCustomerList()
{
    using (DbConnection connection = CreateConnection())
    {
        return connection.Query<CustomerEntity>("procToReturnCustomers", commandType: CommandType.StoredProcedure).ToList();
    }
}

CreateConnection() would handle accessing your db and returning a connection.

Dapper handles mapping datafields to properties automatically. It also supports multiple types and result sets and is very fast.

Query returns IEnumerable hence the ToList().