128 votes

Comment puis-je convertir facilement DataReader à la Liste<T>?

J'ai des données dans un DataReader qui je veux être converti en List<T>. Qu'est ce qu'une simple solution pour cela?

Pour, par exemple, dans CustomerEntity classe, j'ai CustomerId et CustomerName propriétés.Si mon DataReader renvoie ces deux colonnes de données, alors comment puis-je le convertir en List<CustomerEntity>.

216voto

Jon Skeet Points 692016

Je suggère de rédiger une extension de la méthode pour cela:

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

Vous pouvez ensuite utiliser LINQ ToList() méthode pour convertir en List<T> si vous voulez, comme ceci:

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();
}

En fait, je peux suggérer de mettre un FromDataReader méthode de Customer (ou ailleurs):

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

Qui laisserait:

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

(Je n'ai pas pense que l'inférence de type travaille dans ce cas, mais je peux me tromper...)

77voto

Ali YALÇIN Points 61

J'ai écrit la méthode suivante à l'aide de ce cas.

Tout d'abord, ajouter l'espace de noms: System.Reflection

Par Exemple: T est le type de retour(ClassName) et dr est le paramètre de mappage DataReader

C#, Appelez la méthode de mapping comme suit:

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

C'est la méthode de cartographie:

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, Appeler la méthode de mapping comme suit:

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

C'est la méthode de cartographie:

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

53voto

Ian Ringrose Points 19115

J'ai vu des systèmes qui utilisent la Réflexion et les attributs sur les Propriétés ou les champs de cartes DataReaders à des objets. (Un peu comme ce qu'LinqToSql.) Ils sauvent un peu de frappe et peut réduire le nombre d'erreurs lors du codage pour DBNull etc. Une fois que vous mettre en cache le code généré, ils peuvent être plus rapide que la plupart des écrits à la main de code, afin de ne considérer la "haute route" si vous faites beaucoup.

Voir "Une Défense de la Réflexion .NET" pour un exemple de cette.

Vous pouvez ensuite écrire le code comme

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

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

...

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

(AutoMap(), est une méthode d'extension)


@Stilgar, merci pour ce grand commentaire

Si sont en mesure de vous êtes probablement mieux d'utiliser NHibernate, EF ou Linq to Sql, etc Toutefois sur l'ancien projet (ou pour d'autres (parfois) raisons, par exemple, "pas inventé ici", "l'amour de la stockées procs", etc) Il n'est pas toujours possible d'utiliser un ORM, donc un poids plus léger système peut être utile d'avoir les manches"

Si vous avez tous besoin de trop écrire beaucoup de IDataReader boucle, vous verrez l'avantage de réduire le codage (et les erreurs) sans avoir à modifier l'architecture du système sur lequel vous travaillez. Ce n'est pas de dire que c'est une bonne architecture pour commencer..

Je suis en supposant que CustomerDTO ne parviendra pas à sortir de la couche d'accès aux données et des objets composites, etc sera constitué par la couche d'accès aux données à l'aide de la DTO des objets.

34voto

Mohsen Points 602

vous pouvez simplement faire comme la mise en jachère:

var dt=new DataTable();
dt.Load(myDataReader);
list<DataRow> dr=dt.AsEnumerable().ToList();

10voto

Ruben Bartelink Points 23945

Évidemment @Ian Ringroses'thèse centrale que vous devez utiliser une bibliothèque pour c'est le meilleur et l'unique réponse ici (d'où un +1), mais pour un minimum de jetable ou le code de démonstration ici est une illustration concrète de l' @SLaks's subtil commentaire sur @Jon Skeet's plus granulaire (+1) réponse:

public List<XXX> Load( <<args>> )
{
    using ( var connection = CreateConnection() )
    using ( var command = Create<<ListXXX>>Command( <<args>>, connection ) )
    {
        connection.Open();
        using ( var reader = command.ExecuteReader() )
            return reader.Cast<IDataRecord>()
                .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )
                .ToList();
    }
}

Comme en @Jon Skeet's réponse, l'

            .Select( x => new XXX( x.GetString( 0 ), x.GetString( 1 ) ) )

peu peut être extraite dans un helper (j'aime bien les vider dans la classe de requête):

    public static XXX FromDataRecord( this IDataRecord record)
    {
        return new XXX( record.GetString( 0 ), record.GetString( 1 ) );
    }

et utilisé comme:

            .Select( FromDataRecord )

Mise à JOUR le Mar 9 13: Voir aussi Quelques excellentes plus subtiles techniques de codage pour séparer le passe-partout de cette réponse

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X