Comment mapper un DataReader pour des propriétés de la classe et de maintenir la performance?

Préambule:

  1. Toutes les données de chaînes de connexion,
    connexions, etc, sont créés à l'aide de
    DbProviderFactories.
  2. Code est mixte C# et VB.Net à partir de
    plusieurs bibliothèques.

Je suis une cartographie de l'DbDataReader d'entités et d'avoir quelques repères:

[0] retrieved 159180 records in 45135 ms
[1] retrieved 159180 records in 45008 ms
[2] retrieved 159180 records in 44814 ms
[3] retrieved 159180 records in 44987 ms
[4] retrieved 159180 records in 44914 ms
[5] retrieved 159180 records in 45224 ms
[6] retrieved 159180 records in 45829 ms
[7] retrieved 159180 records in 60762 ms
[8] retrieved 159180 records in 52128 ms
[9] retrieved 159180 records in 47982 ms  

C'est une quantité considérable de temps et extrêmement pauvres, considérant que cela prend seulement 17 secondes de requête de Sql Server Management Studio. Mon instruction select:

"SELECT * from tbl_MyTable"

Table contient 43 champs et n'est probablement pas indexé comme il se doit; toutefois, l'exécution d'une sélectionnez tous les, je n'attendrais pas d'indexation à être problématique. Alors ... voici ce que je fais:

Définir une entité:

public class Concept
{
    #region Columns
    [DataParameter("ConceptID", DbType.Int32)]
    public Int32 ConceptID
    { get; set; }
    [DataParameter("ConceptName", DbType.String)]
    public string ConceptName
    { get; set; }
    [DataParameter("ConceptTypeID", DbType.Int32)]
    public Int32 ConceptTypeID
    { get; set; }
    [DataParameter("ActiveYN", DbType.Boolean)]
    public bool ActiveYN
    { get; set; }
    #endregion
}

Requête DataReader:

for (int i = 0; i <= 99; i++)
{
    sw.Start();
    var results = session.QueryReader<Concept>(
        new SqlCommand(command), dr => new Concept());

    sw.Stop();

    Console.WriteLine("[{0}] retrieved {1} records in {2} ms", i, results.Count(), sw.ElapsedMilliseconds);
    sw.Reset();
}

... appelant:

Public Function QueryReader(Of TEntity As {Class, New})(ByVal Command As DbCommand, _
                                                        ByVal Projection As Func(Of DbDataReader, TEntity)) _
                                                        As IEnumerable(Of TEntity)

    Dim list As IEnumerable(Of TEntity)

    Command.Connection = dataReader.NewConnection
    Command.Connection.Open()

    Using _reader As DbDataReader = Command.ExecuteReader()
        list = _reader.Query(Of TEntity)(Projection).ToList()
    End Using

    Command.Connection.Close()

    Return list
End Function

... et de la méthode d'extension QueryReader<T>: modifier le placement de nouveaux TEntity() - merci @Henk

public static IEnumerable<TEntity> Query<TEntity>(this DbDataReader Reader,
    Func<DbDataReader, TEntity> Projection)
    where TEntity : class, new()
{
    //  moving this reflection to another class
    Dictionary<string, PropertyInfo> props;

    while (Reader.Read())
    {
        TEntity entity = new TEntity();

        if (!entities.TryGetValue(typeof(TEntity).ToString(), out props))
        {
            // reflection over TEntity
            props = (from p in entity.GetType().GetProperties()
                     from a in p.GetCustomAttributes(typeof(DataParameterAttribute), false)
                     select p)
                     .ToDictionary(p => p.Name);

            entities.Add(typeof(TEntity).ToString(), props);
        }

        foreach (KeyValuePair<string, PropertyInfo> field in props)
        {
            if (null != Reader[field.Key] && Reader[field.Key] != DBNull.Value)
            { field.Value.SetValue(entity, Reader[field.Key], null); }
        }

        yield return entity;
    }
}

Des suggestions sur l'augmentation des performances serait grandement apprécié ...


Mise à jour

J'ai mis en place dapper-point-net @EtienneT suggéré - voici le temps d'extraction:

[0] retrieved 159180 records in 6874 ms
[1] retrieved 159180 records in 6866 ms
[2] retrieved 159180 records in 6570 ms
[3] retrieved 159180 records in 6785 ms
[4] retrieved 159180 records in 6693 ms
[5] retrieved 159180 records in 6735 ms
[6] retrieved 159180 records in 6627 ms
[7] retrieved 159180 records in 6739 ms
[8] retrieved 159180 records in 6569 ms
[9] retrieved 159180 records in 6666 ms
160K enregistrements avec 43 champs de remplissage d'entités via la réflexion? Je dirais 45 à 50 secondes est assez décent, mais peut-être que je suis trop indulgent.
Je pense que c'est décent ... mais je veux wow - surtout depuis que j'ai utiliser un dictionnaire pour mapper le lecteur[le champ] de la propertyInfo ... mais peut-être que mes attentes sont trop élevées. 😉
Juste pour le fun, essayez d'écrire une ancienne while(Reader.Read()) boucle, Fx 2.0 style. Il devrait vous donner une idée de comment cher toute cette réflexion et d'indirection est.
Mélanger vos boissons? (c# et vb)
malheureusement, oui 🙂 a l'époque j'avais un DAL écrite en VB par un autre développeur et j'ai été la construction de la backend en C#. La beauté de .Net!

OriginalL'auteur IAbstract | 2011-05-26