Mai 2016

Volume 31,numéro 5

Cet article a fait l'objet d'une traduction automatique.

Points de données : Dapper, Entity Framework et les applications hybrides

Par Julie Lerman

Julie LermanVous avez probablement remarqué que j'écris beaucoup sur Entity Framework, le Microsoft relationnelle Mappeur ORM (Object) qui a été le premier accès aux données .NET API depuis 2008. Il existe des autres ORM .NET disponibles, mais une catégorie particulière, micro-ORM, obtient un grand nombre de mentions à hautes performances. La micro-ORM que j'ai entendue mentionné plus est Dapper. Ce que suscité enfin suffisamment mon intérêt à prendre un délai d'expiration pour le trouver open a récemment été divers développeurs qu'ils ont créé des solutions hybrides avec Entity Framework et Dapper, laisser chaque ORM qu'il est préférable à dans une seule application de création de rapports.

Après publication de la lecture de nombreux articles et blogs, discuter avec les développeurs et de s'amuser un peu avec Dapper, je voulais part de mes découvertes avec vous, en particulier ceux qui, comme moi, peut-être entendu parler de Dapper, mais ne savons pas vraiment ce qu'elle fait ou son fonctionnement, ou pourquoi les gens aiment il. N'oubliez pas que je ne suis pas un expert. Au lieu de cela, je connaîtra suffisamment pour satisfaire ma curiosité pour le moment et espère spark votre intérêt donc parlerons davantage.

Pourquoi Dapper ?

Dapper a un historique intéressant, avoir générés à partir d'une ressource, avec que vous pouvez être extrêmement familiarisé : Marc Gravell et Sam Saffron généré Dapper tout en travaillant chez Stack Overflow, résolution des problèmes de performances avec la plate-forme. Un débordement de pile est un site sérieusement à fort trafic a destiné à avoir des problèmes de performances. Selon la page Exchange sur la pile, Stack Overflow possédaient des affichages de page de 5,7 milliards de 2015. En 2011, safran écrit un blog publier sur le travail qu'il avait fait Gravell, intitulé « Comment I appris à arrêter soucis liés et écrire mon propre ORM » (bit.ly), qui explique les problèmes de performances pile a eu à l'heure, la recherche de radical de son utilisation de LINQ to SQL. Il détaille alors pourquoi écrire un ORM personnalisé, un Dapper, était la réponse pour optimiser l'accès aux données sur Stack Overflow. Cinq années plus tard, Dapper est désormais largement utilisée et open source. Gravell et la pile et membre d'équipe Nick Craver continuent à gérer activement le projet à github.com/StackExchange/dapper-dot-net.

Dapper in a Nutshell

Dapper se concentre sur s'affiche pour vous exercez vos compétences SQL pour construire des requêtes et les commandes que vous pensez qu'ils devraient être. Il est plus proche « la pierre » qu'un ORM standard, ce qui décharge l'effort d'interprétation des requêtes telles que LINQ à EF dans SQL. Dapper a certaines fonctionnalités intéressantes de transformations, telles que la possibilité de vous permet d'Explorer une liste passées à une clause WHERE IN. Mais la plupart du temps, le SQL que vous envoyez à Dapper est prêt à l'emploi et les requêtes get pour la base de données beaucoup plus rapidement. Si vous n'aurez à SQL, vous pouvez être que vous écrivez la plupart des commandes performant possible. Vous devez créer un type de IDbConnection, par exemple une connexion SqlConnection avec une chaîne de connexion connus afin d'exécuter les requêtes. Puis, avec son API, Dapper peut exécuter des requêtes pour vous et — fournie par le schéma de résultats de la requête peut être associé avec les propriétés du type cible — automatiquement instancier et de remplir des objets avec les résultats de requête. Il existe un autre avantage notable de performances : Dapper caches efficacement le mappage appris, ce qui entraîne la désérialisation très rapide des requêtes suivantes. La classe que je vais remplir, DapperDesigner (illustré Figure 1), est défini pour gérer les concepteurs qui font très dapper vêtements.

Figure 1 DapperDesigner classe

public class DapperDesigner
{
  public DapperDesigner() {
    Products = new List<Product>();
    Clients = new List<Client>();
  }
  public int Id { get; set; }
  public string LabelName { get; set; }
  public string Founder { get; set; }
  public Dapperness Dapperness { get; set; }
  public List<Client> Clients { get; set; }
  public List<Product> Products { get; set; }
  public ContactInfo ContactInfo { get; set; }
}

Le projet où je suis l'exécution de requêtes comporte une référence à Dapper, lequel récupérer via NuGet (install-package dapper). Voici un appel de l'exemple à partir de Dapper pour exécuter une requête pour toutes les lignes dans la table DapperDesigners :

var designers = sqlConn.Query<DapperDesigner>("select * from DapperDesigners");

Notez que pour les listings de code dans cet article, j'utilise select * plutôt qu'explicitement la projection de colonnes pour les requêtes lorsque je veux que toutes les colonnes d'une table. sqlConn est un objet SqlConnection existant que j'avez déjà instanciée, ainsi que sa chaîne de connexion, mais que vous n'avez pas encore ouvert.

La méthode de requête est une méthode d'extension fournie par Dapper. Lors de l'exécution de cette ligne, Dapper ouvre la connexion, crée un DbCommand, exécute la requête exactement comme je l'ai écrit, instancie un objet DapperDesigner pour chaque ligne dans les résultats et transmet les valeurs à partir des résultats de la requête dans les propriétés des objets. Dapper peut correspondre aux valeurs de résultat avec des propriétés au moyen de quelques modèles même si les noms de colonnes ne correspondent pas les noms de propriété et même si les propriétés ne sont pas dans le même ordre que les colonnes correspondantes. Il ne peut pas lire l'esprit, cependant, alors n'attendez pas à déterminer les mappages impliquant, par exemple, plusieurs valeurs de chaîne dans laquelle les commandes ou les noms des colonnes et des propriétés ne sont pas synchronisées. J'essayer quelques expériences impairs avec lui pour voir comment il répond et il existe également des paramètres globaux qui contrôlent comment Dapper peut déduire des mappages.

Requêtes relationnelles et dapper

Mon type DapperDesigner comportant des relations. Il existe un un-à-plusieurs (avec des produits), une (ContactInfo) un à un et plusieurs-à-plusieurs (Clients). J'ai terminé les exercices de l'exécution de requêtes sur ces relations, et Dapper peut gérer les relations. Il n'est absolument pas aussi simple que d'exprimer une requête LINQ to Entity Framework avec une méthode Include ou même une projection. Mes compétences TSQL ont été envoyées à la limite, cependant, car EF a m'a permis de devenir différé par conséquent, ces dernières années.

Voici un exemple d'interrogation de la relation un-à-plusieurs à l'aide de SQL, j'utiliserais droite dans la base de données :

var sql = @"select * from DapperDesigners D
           JOIN Products P
           ON P.DapperDesignerId = D.Id";
var designers= conn.Query<DapperDesigner, Product,DapperDesigner>
(sql,(designer, product) => { designer.Products.Add(product);
                              return designer; });

Notez que la méthode de requête nécessite de spécifier les types qui doivent être construits, mais aussi indiquent le type à retourner, exprimée par le paramètre de type final (DapperDesigner). Utiliser une expression lambda multiligne pour d'abord les graphiques, ajout de produits en cause à leurs objets de concepteur parent, puis pour retourner chaque concepteur à l'interface IEnumerable la méthode Query renvoie.

L'inconvénient de cette opération avec ma meilleure tentative SQL est que les résultats sont aplatis, tout comme ils le seraient avec la méthode EF inclure. J'y reviendrai une ligne par produit avec les concepteurs dupliqués. Dapper possède une méthode MultiQuery qui peut retourner plusieurs jeux de résultats. Combiné avec GridReader de Dapper, les performances de ces requêtes seront sans aucun doute surpasser Entity Framework inclut.

Plus difficile à du Code, plus rapide à exécuter

Expression SQL et de remplir des objets connexes sont des tâches que j'ai permettent de gérer en arrière-plan, c'est sans aucun doute plus d'efforts au code Entity Framework. Mais si vous travaillez avec grandes quantités de données et les performances d'exécution sont important, elles peuvent vous être intéressant de cet effort. J'ai environ 30 000 concepteurs dans ma base de données exemple. Seules quelques-unes d'entre elles ont des produits. J'ai fait des tests de banc d'essai simple où j'ai vérifié que je me suis comparaison pommes de pommes. Avant d'examiner les résultats des tests, il existe quelques points importants à comprendre comment je l'ai fait ces mesures.

N'oubliez pas que, par défaut, Entity Framework est conçu pour effectuer le suivi des objets qui sont les résultats de requêtes. Cela signifie qu'il crée des objets de suivi supplémentaires, qui implique des efforts et il doit également interagir avec les objets de suivi. Dapper, en revanche, juste des dumps résultats en mémoire. Par conséquent, il est important du suivi des modifications d'Entity Framework prennent hors de la boucle lors des comparaisons de performances. Faire cela en définissant toutes mes requêtes Entity Framework avec la méthode AsNoTracking. En outre, lors de la comparaison des performances, vous devez appliquer un certain nombre de modèles de banc d'essai standard, telles que la base de données mise en route, répétez la requête autant de fois et en vidant les heures les plus lentes et plus rapide. Vous pouvez voir les détails de comment j'ai créé mes tests d'évaluation dans le téléchargement de l'exemple. Cependant, je considère que ces tests d'évaluation « léger » simplement pour vous donnent une idée des différences. Des bancs d'essai graves, vous devez itérer au sein de nombreuses fois à mon 25 (démarrage à 500) et les performances du système sur lequel vous exécutez. J'exécute ces tests sur un ordinateur portable à l'aide d'une instance de la base de données SQL Server locale, Mes résultats sont utiles uniquement pour les raisons de comparaison.

Les temps que je suis dans mes tests sont pour l'exécution de la requête et les résultats de la génération. L'instanciation des connexions ou DbContexts n'est pas pris en compte. DbContext est réutilisé pour l'heure d'Entity Framework générer le modèle en mémoire n'est également pas pris en compte, comme cela se produit uniquement une fois par instance d'application, pas pour chaque requête.

Figure 2 montre la « sélectionner * » teste pour Dapper et LINQ EF de requête afin de voir la construction de base de mon modèle de test. Notez qu'en dehors de l'heure réelle de collecte, que je regroupe à la fois pour chaque itération dans une liste (appelée « heures ») pour une analyse plus approfondie.

Figure 2 Tests pour comparer EF et Dapper lorsqu'interroger tous les DapperDesigners

[TestMethod,TestCategory("EF"),TestCategory("EF,NoTrack")]
public void GetAllDesignersAsNoTracking() {
  List<long> times = new List<long>();
  for (int i = 0; i < 25; i++) {
    using (var context = new DapperDesignerContext()) {
      _sw.Reset();
      _sw.Start();
      var designers = context.Designers.AsNoTracking().ToList();
      _sw.Stop();
      times.Add(_sw.ElapsedMilliseconds);
      _trackedObjects = context.ChangeTracker.Entries().Count();
    }
  }
  var analyzer = new TimeAnalyzer(times);
  Assert.IsTrue(true);
}
[TestMethod,TestCategory("Dapper")
public void GetAllDesigners() {
  List<long> times = new List<long>();
  for (int i = 0; i < 25; i++) {
    using (var conn = Utils.CreateOpenConnection()) {
      _sw.Reset();
      _sw.Start();
      var designers = conn.Query<DapperDesigner>("select * from DapperDesigners");
      _sw.Stop();
      times.Add(_sw.ElapsedMilliseconds);
      _retrievedObjects = designers.Count();
    }
  }
  var analyzer = new TimeAnalyzer(times);
  Assert.IsTrue(true);
}

Il existe un autre point à effectuer sur la comparaison « pommes de pommes ». Prend dapper dans SQL brut. Par défaut, les requêtes Entity Framework sont exprimées avec LINQ to Entity Framework et doivent passer par des efforts pour générer le code SQL pour vous. Une fois que SQL est généré, même les SQL s'appuie sur les paramètres, il est mis en cache dans la mémoire de l'application afin que l'effort est réduit à répétition. En outre, EF a la possibilité d'exécuter des requêtes à l'aide de SQL brut, donc j'ai pris les deux approches en compte. Figure 3 répertorie les résultats comparés des quatre jeux de tests. Le téléchargement contient d'autres tests.

Figure 3 le temps moyen en millisecondes pour exécuter une requête et de remplir un objet basé sur 25 itérations, éliminant le plus rapide et plus lente

* AsNoTracking des requêtes Relation LINQ à EF * Entity Framework bruts SQL * Dapper brute SQL
Tous les concepteurs (lignes 30 Ko) 96 98 77
Tous les concepteurs de produits (lignes 30 Ko) 1 : * 251 107 91
Tous les concepteurs de Clients (lignes 30 Ko) * : * 255 106 63
Tous les concepteurs de Contact (lignes 30 Ko) 1 : 1 322 122 116

 

Dans les scénarios illustrés Figure 3, il est facile de concevoir un cas d'utilisation de Dapper par LINQ to Entities. Mais les différences entre les requêtes SQL bruts étroits ne justifient pas toujours basculer vers Dapper pour effectuer une tâche particulière dans un système où vous utilisez sinon Entity Framework. Bien entendu, vos besoins seront différents et peut affecter le degré de variation entre les requêtes Entity Framework et Dapper. Toutefois, dans un système de fort trafic, telles que Stack Overflow, même quelques millisecondes enregistrés par la requête peuvent être critique.

Dapper et EF pour d'autres besoins de persistance

Jusqu'à présent, j'ai mesurée où j'ai simplement extraits retour toutes les colonnes d'une table qui correspond exactement aux propriétés des types de renvoi de requêtes simples. Qu'en est-il si vous êtes projection des requêtes dans les types ? Tant que le schéma des résultats correspond au type, Dapper ne voit aucune différence dans la création des objets. Toutefois, EF, doit travailler plus difficile si les résultats de la projection ne sont pas alignées avec un type qui fait partie du modèle.

Le DapperDesignerContext a un DbSet pour le type DapperDesigner. J'ai un autre type dans mon système appelé MiniDesigner qui possède un sous-ensemble de propriétés de DapperDesigner :

public class MiniDesigner {
    public int Id { get; set; }
    public string Name { get; set; }
    public string FoundedBy { get; set; }
  }

MiniDesigner ne fait pas partie de mon modèle de données Entity Framework, donc DapperDesignerContext n'a aucune connaissance de ce type. J'ai trouvé qu'interrogation toutes les 30 000 lignes et la projection dans les 30 000 MiniDesigner les objets de 25 % plus rapide avec Dapper qu'avec Entity Framework utilisait SQL brut. Là encore, je vous recommande de faire votre propre pour prendre des décisions pour votre propre système de profilage des performances.

Dapper peut également être utilisé pour envoyer des données dans la base de données avec des méthodes qui vous permettent d'identifier les propriétés doivent être utilisées pour les paramètres spécifiés par la commande, si vous utilisez une insertion brute ou la commande de mise à jour ou si vous exécutez une procédure stockée ou fonction sur la base de données. Je ne le faites pas toutes les comparaisons de performances pour effectuer ces tâches.

Hybride Dapper Plus EF dans le monde réel

Il existe un grand nombre et un grand nombre de systèmes qui utilisent Dapper à 100 % de leur persistance des données. Mais rappelez-vous que mon intérêt a suscité en raison des développeurs parler des solutions hybrides. Dans certains cas, il s'agit de systèmes qui disposent EF et cherchent à ajuster les zones à problème particulier. Dans d'autres, les équipes ont choisi d'utiliser Dapper pour toutes les requêtes et d'Entity Framework pour tous les enregistrements.

En réponse à ma demande à ce sujet sur Twitter, j'ai reçu des commentaires variés.

@garypochron m'a dit que son équipe était « déplacement avec à l'aide de Dapper dans les zones d'appel élevé et utilisation des fichiers de ressources pour maintenir l'organisation de SQL ». J'ai été surpris d'apprendre que Simon Hughes (@s1monhughes), l'auteur du populaire EF inverser POCO générateur, passe la direction opposée, valeur par défaut : Dapper et l'utilisation d'EF pour des problèmes difficiles. Il m'a dit « utiliser Dapper lorsque cela est possible. S'il s'agit d'une mise à jour complexe utiliser Entity Framework. »

J'ai vu également une variété de discussions où l'approche hybride est piloté par la séparation des préoccupations plutôt que d'amélioration des performances. Les plus courantes tirer parti de la fiabilité de la valeur par défaut d'ASP.NET Identity sur Entity Framework et ensuite utilisent Dapper pour le reste de la persistance de la solution.

Utilisation plus directement avec la base de données présente d'autres avantages en plus de performances. Rob Sullivan (@datachomp) et Mike Campbell (@angrypets), les deux experts SQL Server, adore Dapper. Rob souligne que vous faire bénéficier des fonctionnalités de base de données qu'Entity Framework ne donne pas accès, telles que la recherche de texte intégral. À long terme, cette fonctionnalité est, en effet, sur les performances.

En revanche, il existe des opérations que réalisables avec Entity Framework que vous ne pouvez pas faire avec Dapper outre le suivi des modifications. Un bon exemple est un j'ai tiré parti de lors de la génération de la solution que j'ai créé pour cet article : la possibilité de migrer votre base de données en tant que les modifications apportées au modèle à l'aide d'Entity Framework Code First Migrations.

Dapper n'est pas bien que pour tout le monde. @damiangray m'a dit que dapper n'est pas une option pour sa solution, car il doit être en mesure de retourner des IQueryables à partir d'une partie de son système à un autre, pas les données. Cette rubrique, l'exécution de requête différée, a été soulevée dans le référentiel de GitHub de Dapper à bit.ly/22CJzJl, si vous souhaitez en savoir plus à ce sujet. Lorsque vous concevez un système hybride, à l'aide d'une version de commande requête séparation (CQS) vous permet de concevoir des modèles distincts pour des types particuliers de transactions (quelque chose que je suis un fan de) est un bon chemin d'accès. De cette façon, vous ne sont pas tentez de générer le code d'accès aux données qui est suffisamment vanille avec Entity Framework et Dapper, ce qui entraîne souvent sacrifier les avantages de chaque ORM. Comme je travaillais sur cet article, Kurt Dowswell publié un billet appelée « Dapper, EF et CQS » (bit.ly/1LEjYvA). Pratique pour moi et pratique pour vous.

Pour ceux qui souhaitent poursuivre CoreCLR et ASP.NET Core, Dapper a évolué pour prendre en charge pour ces derniers, ainsi. Vous trouverez plus d'informations dans un thread dans le référentiel de GitHub de Dapper à bit.ly/1T5m5Ko.

Par conséquent, pour finir, j'ai examiné Dapper. Quel est mon avis ?

Et qu'en est-il de moi ? Je regrettons ne pas prendre le temps d'examiner Dapper précédemment et suis heureux, que j'ai enfin fait. Je vous recommande toujours d'AsNoTracking ou à l'aide des vues ou des procédures dans la base de données afin d'atténuer les problèmes de performances. Cela n'a jamais me ou mes clients. Mais maintenant savoir ont une autre astuce de ma chemise de recommander aux développeurs qui souhaitent tirant encore améliorer les performances de leurs systèmes qui utilisent Entity Framework. Il n'est pas un shoo-in, comme on dit. Ma recommandation sera d'Explorer Dapper, de mesurer la différence de performances (à l'échelle) et de trouver un équilibre entre performances et la facilité de codage. Envisagez d'utiliser évident de StackOverflow : interrogation des questions, commentaires et réponses, puis renvoyer des graphiques d'une question avec ses commentaires et les réponses, ainsi que des métadonnées (modifications) et les informations utilisateur. Elles sont effectuant les mêmes types de requêtes et mappage indéfiniment la même forme des résultats. Dapper est conçu pour briller avec ce type de requêtes répétitives, mise en route rapidement et plus efficacement chaque fois. Même si vous n'avez pas un système avec le nombre de transactions Dapper a été conçu pour fou, vous trouverez certainement qu'une solution hybride vous donne exactement ce que vous avez besoin.


Julie Lermanest Microsoft MVP, mentor et conseillère qui habite dans les collines du Vermont .NET. Vous pouvez trouver sa présentation sur l'accès aux données et d'autres rubriques .NET à des groupes d'utilisateurs et à des conférences dans le monde entier. À l'adresse thedatafarm.com et est l'auteur de « Programming Entity Framework », ainsi qu'un Code First et une édition DbContext, à partir d'o ' Reilly Media. Vous pouvez la suivre sur Twitter : @julielerman et consulter ses cours Pluralsight sur juliel.me/PS-Videos.

Grâce aux Stack Overflow experts techniques suivants d'avoir relu cet article : Nick Craver et Marc Gravell
Nick Craver (@Nick_Craver) est un développeur, ingénieur de fiabilité de Site et parfois DBA de débordement de pile. Il est spécialisé dans le réglage des performances dans toutes les couches, architecture générale du système, matériel du centre de données et mise à jour de plusieurs projets open source telles que Opserver. Rechercher à l'adresse

Marc Gravell est développeur chez Stack Overflow, en particulier sur les bibliothèques de hautes performances et des outils pour .NET, en particulier d'accès aux données, la sérialisation et API de réseau, qui contribuent à une gamme de projets open source dans ces domaines.