EF6 : Tips&Tricks

EF Core va bientôt prendre la relève mais je pense que EF6 va continué son chemin pendant encore un bon bout de temps avant de disparaître ! C’est pourquoi je me suis dit que j’allais partager avec vous toutes les petites astuces que j’ai découvert ces dernières années.

I. Performances

1) READ_COMMITTED_SNAPSHOT

C’est un paramètre SQL Server au niveau de la base de donnée qui est mis de base en code first, et qui permet « potentiellement » une meilleur scalabilité et moins de deadlocks.

En gros, ça crée un snapshot de la base en read-only sur lequel les SELECT seront executé. Quand un changement s’effectue (par ex : un UPDATE) il s’effectue sur la base (et non sur le snapshot) et un nouveau snapshot de la base est créé.

Ce paramètre est particulièrement adapté lorsque la majorité des requêtes effectuées sont des SELECT.

Commande :

ALTER DATABASE <dbname> SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE <dbname> SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE <dbname> SET MULTI_USER;

2) Warm-up

Lors de la 1ère requête à un Model, EF fait beaucoup de choses avant de pouvoir l’exécuter, cela peut facilement prendre 5-6sec !

Afin d’éviter cela vous pouvez améliorer les choses à plusieurs niveaux, mais personnellement j’ai choisi de contourner le problème en maintenant à chaud mon site sous IIS. Voir cet article de blog pour plus d’infos.

Vous pouvez aussi décider d’utiliser NGEN qui permet d’éviter que l’assembly d’EF (qui est grosse, environ 5Mo) soit JIT à chaque lancement. On peut gagner environ 1-3secondes.

Vous pouvez aussi consulter cet article MSDN pour en savoir plus ce qui se passe et les différentes pistes d’amélioration possibles.

3) Les méthodes async

Sur un site web ou une Web Api le nombre de thread est limité et l’utilisation des méthodes Async de EF permet de libérer le thread courant afin qu’il commence le traitement d’une requête le temps que la base de donnée retourne le résultat.

Plus d’infos sur cet article de blog ou cet article msdn.

4) NoTracking

Lorsque vous faites une requête avec EF, l’état de l’objet est ensuite tracké par le contexte. Régulièrement on n’a pas besoin de ce tracking et cela entraine juste une hausse des consommations CPU/RAM et une perte de performance, surtout dans le cadre de recherche où le nombre d’items retournés est élevé !

Pour éviter de tracker, rien de plus simple, il suffit de rajouter AsNoTracking() à la fin de la query !

Exemple :

var blogs2 = context.Blogs
 .Where(b => b.Name.Contains(".NET"))
 .AsNoTracking()
 .ToList();

5) NCache

NCache est passé OpenSource et peut servir de cache de 2nd niveau pour EF.

6) Break queries

Afin d’améliorer les performances il est souvent préférable de découper une grosse requête en plusieurs petites afin de limiter le nombre d’include.

7) Optimiser les Update via des jointures conditionnelles

Le cas standard d’un update avec EF est le suivant :

L’utilisateur modifie quelques champs -> le client envoie au serveur l’entité entière -> on récupère le graph complet de l’entité côté serveur -> on mappe les champs -> on sauvegarde

Une optimisation courante est donc d’effectuer uniquement les jointures dont on a besoin pour mettre à jour l’entité en base.

La détection du changement peut être faite via le calcul des hashs des entités enfants côté serveur au moment du Get. On recalculera ces mêmes hashs au moment de la sauvegarde et on comparera les hash entre le Get et l’Update pour voir si l’entité enfant doit être mise à jour, et donc si la jointure doit être faite.  Les hashs seront stockés dans une variable au niveau de chaque entité enfants. Pour économiser des cycles CPU serveurs ou de la bande passante ce calcul de hash peut être déporté sur le client.

Exemple

 IQueryable<CLIENT> query = _database.CLIENT; 
 if (clsPersonne.hashIdentiteContactPhysiqueAvant != clsPersonne.hashIdentiteContactPhysiqueApres) 
 query = query.Include(x => x.IDENTITE_CONTACT_PHYSIQUE); 
 ...
 ...
 var client = query.SingleOrDefault(x => x.Id = clsPersonne.Id);

8) Entity Framework Extended Library

Librairie pour EF permettant d’ajouter des fonctionnalités à EF comme les Batch Update/Delete (de base dans EF, on ne peut mettre à jour qu’une ligne par requête).

Permet aussi de gérer un cache simpliste.

9) Au cas où…

N’oublier pas de vérifier vos index et que vos types de données correspondent !

 

2. Les requêtes

1) Mémo pour gérer les graphs d’entités

 

2) Find au lieu de SingleOrDefault

La méthode Find de DbSet permet d’aller récupérer une entité du contexte via sa Primary Key, si l’élément n’est pas trouvé dans le contexte, l’élément est récupéré depuis la base.

Alors qu’avec SingleOrDefault la requête sera toujours exécutée.

3) Logger / Intercepter les requêtes SQL

Une nouveauté peu connue de EF 6.1 est de pouvoir logger les requêtes SQL très facilement, il suffit d’ajouter dans le .config :

<interceptors>
 <interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
 <parameters>
 <parameter value="C:\Temp\LogOutput.txt"/>
 <parameter value="false" type="System.Boolean"/>
 </parameters>
 </interceptor>
 </interceptors>
  • Booléen à False : Le fichier est réécrit à chaque fois que l’application redémarre
  • Booléen à True: Le texte est ajouté à la fin du fichier

4) Func<> vs Expression<Func<>>

Si on souhaite réaliser un filtre, par exemple:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)

{

return x => x.Name.Length > length;

}

On l’utilise de la manière suivante :

context.Table.Where(FilterByNameLength(500));

Si vous passer une Func<> à une query EF (pour appliquer un filtre par exemple), la méthode ne sera pas traduite en SQL et sera appliquée a posteriori ce qui est rarement ce que l’on souhaite.

Alors que si on utile une Expression<Func<>> la méthode sera bien traduite en SQL.

5) eSQL

Entity SQL (eSQL) est un language indépendant du stockage permettant de récupérer des données sous formes d’objets ou de forme tabulaire. Cela peut être très pratique pour créer des requêtes dynamiques.

Exemple :

using (AdventureWorksEntities context =
    new AdventureWorksEntities())
{
    string esqlQuery = @"SELECT VALUE Contact
        FROM AdventureWorksEntities.Contacts as Contact where Contact.LastName = @ln";

    // The following query returns a collection of Contact objects.
    ObjectQuery<Contact> query = new ObjectQuery<Contact>(esqlQuery, context, MergeOption.NoTracking);
    query.Parameters.Add(new ObjectParameter("ln", "Zhou"));

    // Iterate through the collection of Contact items.
    foreach (Contact result in query)
        Console.WriteLine("Contact First Name: {0}; Last Name: {1}",
                result.FirstName, result.LastName);
}

6) UnitOfWork

C’est un design pattern qui permet d’abstraire toute la couche de persistence tout en évitant de réinstancier le DBContext à chaque opération (ce qui permet aussi de faire des transactions très simplement). Voir cet article msdn pour plus d’informations.

7) DbFunctions

Cette méthode possède des fonctions utilitaires afin d’effectuer des opérations sur les dates au sein des requêtes EF. Voir la doc MSDN.

8) Jointures externes

Pour cela il faut utiliser DefaultIfEmpty, on l’utilise de la manière suivante :

var q = from c in categories

join p in products on c equals p.Category into ps

from p in ps.DefaultIfEmpty()

select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };

III. Divers

1) EF Power Tools

C’est une extension Visual Studio qui permet notamment de :

  • Reverse Engineer Code First : Génère le DBContext, les POCO et le mapping Code First à partir d’une base de données existante (option de base dans VS 2015, via l’ajout d’un modèle ADO.Net)
  • Customize Reverse Engineer Templates : Ajoute le template T4 à votre projet afin de pouvoir customiser la génération de code
  • View Entity Data Model (Read-only) : Affiche une vue du modèle Code First dans le EF designer?
  • View Entity Data Model XML : Affiche l’EDMX XML représentant le Code First.
  • Generate Views : Génère des vues précompilées afin d’améliorer le temps de démarrage de l’application

Plus d’infos ici : https://msdn.microsoft.com/en-us/data/jj593170.aspx

2) Pluralization Service

Vous avez déjà dû le remarquer mais EF possède un algorithme pour mettre aux pluriels les variables, il supporte seulement l’anglais mais vous pouvez faire votre service custom et le pluger à Entity Framework.

Voir cet article pour plus d’infos

 

Voilà, j’espère que vous avez appris quelques petits trucs qui vous faciliteront la vie 😉

Entity Framework : Code First avec Migration 2/2

La 1ère partie de l’article concernait l’implémentation de Code First Migration.

Dans cette 2ème partie d’article sur La migration Entity Framework, nous allons voir:

  • Comment insérer ou mettre à jour des données en même temps que les changements de la structure de la base de données.
  • Configurer la migration pour qu’elle s’effectue automatiquement.
  1. Migration sur les données

    Dans la classe de configuration qui a été créé lors de l’activation de la migration, il y a une méthode Seed (créé par défaut) qui va permettre d’insérer des données.

    En effet lors des migrations il se peut que le (ou les) tables doivent être détruites pour être reconstruite, donc éviter de perdre des données il est possible de insérer dans la configuration de migration.

    Pour cela il faut utiliser la méthode AddOrUpdate qui a pour avantage de ne pas faire uniquement des insert mais s’il l’enregistrement existe déjà elle fera un update (ca évide donc d’avoir des doublons).


    Il y aussi une autre possibilité c’est d’utiliser un script SQL, et de l’exécuter dans la méthode seed, par exemple :

  2. Migration automatique

    La migration automatique permet d’appliquer automatiquement les changements à la base de données lors de l’exécution de l’application.

    Pour l’activer il faut:

  • modifier la propriétés AutomaticMigrationsEnabled et la renseigner à true, et rajouter une autre propriété AutomaticMigrationDataLossAllowed = true car la migration automatique ne permet pas la perte de données. On a donc dans la configuration :


  • Implémenter la méthode OnModelCreating:

    Dans la classe configuration il faut rajouter une méthode OnModelCreating avec :

    Database.SetInitializer(new MigrateDatabaseToLatestVersion<MvcApplicationEFCodeFirst.Models.MvcMusicStoreEntities, Configuration>());

    Ce qui nous donne:


Et voilà lors de l’exécution de l’application, la base de données sera mise à jour à partir du model.
Remarques sur la migration automatique:
La migration automatique peut se révéler pratique dans un cadre de développement, par contre à mon sens elle n’est pas à être utilisée en production car lors de modification de la base des données peuvent être perdues.

Entity Framework : Code First avec Migration 1/2

Entity Framework est pour rappel un ORM (Object RelationShip Manager) qui permet de faire :

  • Le mapping entre la base de données et les classes Entités
  • De proposer les méthodes du CRUD (Create Read Update Delete)

Il existe 3 workflows :

  • dataBase First
  • Model First
  • Code First

Lors de la création d’un projet MVC 4 dans Visual Studio 2012 , EF 5 est intégré par défaut
Cet article a pour but d’expliquer comme implémenter Entity Framework en utilisant le workflow Code First (et générer la base de données)

  1. Code First

    Le workflow Code First consiste à écrire comme son nom l’indique en 1er les classes entités (qui représentent le Model) puis:

  • soit de faire le mapping avec une base de données déjà existante
  • soit à partir de ces dernières de générer la base de données.
  1. Les classes Entités (Model)

    Dans notre exemple nous travaillons sur une application simple de gestion de Music (Music Store)
    Nous notre projets MVC dans le répertoire Models nous allons créer 2 classes qui représentent nos entités comme suit :

    Album.cs

    Genres.cs

  2. La classe de mapping (DbContext)

    Dans Models on crée une classe MvcMusicStoreEntities.cs qui hérite de DbContext, ça représente notre DBcontext



    Dans notre exemple on a fait le mapping :

    Table Albums –> classe Album

    Table Genres –> classe Genre

Lire la suite