Comment mapper un DataReader pour des propriétés de la classe et de maintenir la performance?
Préambule:
- Toutes les données de chaînes de connexion,
connexions, etc, sont créés à l'aide de
DbProviderFactories. - 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
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
Vous devez vous connecter pour publier un commentaire.
Avez-vous considéré comme un micro ORM comme dapper.net?
https://github.com/StackExchange/dapper-dot-net
Elle est faite par les développeurs de StackOverflow et carte une requête SQL directement à vos objets. Il génère et les caches IL code pour la carte SQL résultats de vos objets. Ainsi, le code IL est généré qu'une seule fois par type. Jamais utilisé, mais si vous avez besoin de performances de votre carte SQL résultats .net des objets, c'est la bibliothèque dont vous avez besoin.
Je suis à la recherche d'elle - je lui donner un joli test rapide, je pense que je vais poster quelques repères dès que je l'ai terminé.
Quelque chose de semblable faite par Rob Conery: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive Aussi, assez rapide, mais il traite plus avec les objets dynamiques. Dapper est toujours plus rapide que ce que j'ai lu.
Seule chose négative que j'ai vu sur Dapper est qu'il génère, IL mapper vos objets, puis de les mettre en cache. Donc si il y a un bug dans l'généré IL, bonne chance. Regardez la dapper code: github.com/SamSaffron/dapper-dot-net/blob/master/Dapper/...
Jusqu'à présent, il semble être assez rapide et sans problème. Je n'attends pas beaucoup de la manière de IL des bugs être que Marc Gravel est impliqué.
OriginalL'auteur EtienneT