Blog ENI : Toute la veille numérique !
Jusqu'à ce soir ! : -25€ dès 75€ sur les livres en ligne, vidéos... code FUSEE25. J'en profite !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Entity Framework Core
  3. Des objets au SQL
Extrait - Entity Framework Core Maîtrisez la solution de Mappage Objet-Relationnel de Microsoft
Extraits du livre
Entity Framework Core Maîtrisez la solution de Mappage Objet-Relationnel de Microsoft Revenir à la page d'achat du livre

Des objets au SQL

Modèle d’étude

La quasi-totalité des exemples qui sont décrits dans ce chapitre sont fondés sur la base de données de démonstration Northwind de Microsoft. Il est donc plus que probable que vous ayez besoin d’obtenir une copie locale de cette base si vous souhaitez tester les portions de code que vous découvrirez tout au long de ce chapitre.

Le code source relatif à cet ouvrage, disponible en téléchargement depuis la page Informations générales, contient des scripts SQL qui vous permettront de remplir de véritables sources de données. Ces scripts sont adaptés à différents fournisseurs de données, en fonction de votre préférence :

  • SQL Server (testé avec SQL Server 2014 Express LocalDB) ;

  • SQLite (testé avec SQLite 3.11.0) ;

  • SQL Server Compact Edition 4.0.

Ils sont localisés dans le répertoire Chapitre 03/Scripts et portent le nom de la source de données à laquelle ils sont associés.

Le contexte de données associé à cette base de données, dans son format SQL Server, est décrit ci-après, ainsi que les types d’entités qui sont nécessaires à son bon fonctionnement.


public partial class NorthwindContext : DbContext 
{ 
    public virtual DbSet<Categories> Categories { get; set; } 
    public virtual DbSet<CustomerCustomerDemo> CustomerCustomerDemo { get; set; } 
    public virtual DbSet<CustomerDemographics> CustomerDemographics { get; set; } 
    public virtual DbSet<Customers> Customers { get; set; } 
    public virtual DbSet<EmployeeTerritories> EmployeeTerritories { get; set; } 
    public virtual DbSet<Employees> Employees { get; set; } 
    public virtual DbSet<OrderDetails> OrderDetails { get; set; } 
    public virtual DbSet<Orders> Orders { get; set; } 
    public virtual DbSet<Products> Products { get; set; } 
    public virtual DbSet<Region> Region { get; set; } 
    public virtual DbSet<Shippers> Shippers { get; set; } 
    public virtual...

Fournisseurs de données

Les fournisseurs de données sont des librairies complémentaires pour Entity Framework Core qui ont la responsabilité de l’implémentation des fonctionnalités propres à la source de données sur laquelle elles opèrent.

Le principe clé derrière l’existence des fournisseurs de données est l’isolation des traitements spécifiques aux sources de données. Entity Framework Core implémente pour cela une librairie centrale, qui contient les éléments généraux, et qui attend des implémentations particulières de traitements qui sont, eux, gérés par les fournisseurs de données. Ces derniers peuvent donc, d’un point de vue purement technique, être considérés comme des plug-ins, avec la condition suivante : il est nécessaire qu’au moins une de ces librairies d’extension soit présente pour tirer parti de la librairie centrale. Dans le cas contraire, rien ne peut fonctionner.

Conformément à ce que nous venons de voir, les fournisseurs de données pour Entity Framework Core sont livrés sous la forme d’assemblies complémentaires. Ils sont intégrés à une application par l’intermédiaire du gestionnaire de package NuGet. Au jour de l’écriture de ces lignes...

Requêtage avec LINQ

Avec la sortie du framework .NET en version 3.5 en fin d’année 2007, Microsoft a introduit une nouvelle manière de manipuler les données, qu’elles soient locales ou issues d’une source de données externe. Cette petite révolution se présente sous un nom simple : LINQ (Language INtegrated-Query). Cette interface permet d’interroger toutes les sources de données compatibles d’une manière complètement unifiée, tout en offrant l’avantage non négligeable de vérifier la validité des requêtes dès la phase de compilation.

1. API et extension à C#

LINQ est une brique logicielle constituée, d’une part, d’une bibliothèque de classes fournissant des capacités de requêtage et, d’autre part, d’un jeu d’extensions pour le langage C# permettant l’utilisation de mots-clés spécifiques pour l’écriture de requêtes avec une syntaxe proche de celle de SQL.

L’extension du langage est un sucre syntaxique qui vise à simplifier l’écriture des requêtes, mais les expressions utilisant la syntaxe LINQ sont traduites par le compilateur en appels de méthodes conventionnels. On peut en déduire que toute requête LINQ peut être réécrite avec des appels de méthodes. L’inverse n’est pas vrai, certaines méthodes n’ayant pas d’équivalent dans la syntaxe LINQ. Il n’existe notamment aucun mot-clé traduisant la méthode FirstOrDefault dans l’extension LINQ de C#.

L’existence de ces deux syntaxes a pour avantage de laisser au développeur le choix d’utiliser celle avec laquelle il se sent le plus à l’aise. Certains préfèrent en effet écrire des requêtes de manière fluide en n’utilisant que des mots-clés du langage. D’autres, dont l’auteur de cet ouvrage, ont plus d’affinités avec l’utilisation de fonctions et de procédures.

LINQ est capable de travailler sur différentes sources de données allant de la collection d’objets...

LINQ dans le détail

Dans un monde de plus en plus gouverné par les données, la simplicité de LINQ est un atout non négligeable pour l’écriture d’applications robustes et efficaces. Sa puissance est d’autant plus grande qu’il propose de nombreux opérateurs permettant d’effectuer une grande variété de traitements.

Tous les opérateurs définis par LINQ sont disponibles sous la forme de méthodes d’extension utilisables sur les objets de type IEnumerable<T>. Dans le cas d’Entity Framework, les méthodes utilisées sont plus exactement redéfinies sur le type IQueryable<T>, qui hérite notamment de IEnumerable<T>. Nous commencerons par expliquer la différence entre ces deux interfaces, puis nous détaillerons les différents opérateurs définis par Entity Framework Core. Lorsque l’un d’eux est disponible aussi bien sous la forme d’une méthode qu’en tant que mot-clé du langage C#, les deux modes d’écriture seront détaillés.

1. IEnumerable<T> et IQueryable<T> : les différences

Depuis sa toute première version, la bibliothèque de classes du framework .NET intègre une interface nommée IEnumerable, dont l’objectif est de fournir l’infrastructure nécessaire à l’exécution du code généré par l’utilisation de boucles foreach. Le code IL (Intermediate Language) associé à ces boucles utilise en effet la méthode GetEnumerator() du type IEnumerable pour obtenir un objet capable de référencer chaque élément de la collection. Le pendant générique de cette interface, IEnumerable<T>, a fait son apparition avec l’arrivée de .NET 2.0, toujours dans le même objectif, mais en ajoutant le support du typage fort sur les éléments de la collection itérée.

La librairie System.Linq, présente depuis le framework 3.5, inclut quant à elle une interface nommée IQueryable<T>, qui est destinée à fournir un point d’entrée pour l’extension de LINQ à l’aide de fournisseurs de données. Cette interface hérite...

Cycle de vie des entités

De leur création à leur suppression, les entités passent par différents états qui déterminent la manière dont Entity Framework doit les traiter. Nous verrons dans cette section quels sont ces états et comment les manipuler. Nous jetterons également un œil de l’autre côté du miroir pour comprendre comment Entity Framework Core les gère, puis pour agir de manière à mettre les entités hors de sa portée, ou au contraire, à les placer sous sa surveillance.

1. Cinq types de manipulations, cinq états

Les différentes manipulations de données effectuées au travers d’Entity Framework sont orchestrées à l’aide de différents composants, dont le Change Tracker (que l’on pourrait traduire par "Surveillant des modifications"). Avant de détailler son rôle et son fonctionnement, il est important de comprendre les états que peut avoir une entité, ainsi que les actions qui mènent à son changement d’état.

L’énumération Microsoft.EntityFrameworkCore.EntityState définit cinq valeurs qui couvrent l’ensemble du cycle de vie des entités.

Detached

Cette valeur est associée aux entités qui ne sont pas attachées à un contexte de données. L’état associé à une entité est généralement égal à Detached lorsque l’entité a été supprimée de la source de donnée (à l’aide d’Entity Framework) ou que le développeur détache impérativement l’entité. Il est également possible d’arriver à cet état par une demande de suppression d’une entité qui n’a pas d’enregistrement associé au niveau de la source de données, mais qui est sous la surveillance d’un contexte de données (état Added).

Unchanged

L’état d’une entité est défini à Unchanged lorsqu’elle est attachée à un contexte de données et que son contenu est identique à celui de l’enregistrement qu’elle représente. C’est notamment le cas juste après...

Validation des changements

Si les actions d’ajout, de mise à jour ou de suppression par les méthodes Add, Update et Remove du contexte modifient l’état d’un objet, elles ne sont toutefois d’aucune utilité sans leur répercussion sur la source de données. Cette dernière tâche, la validation des changements, est assurée par la méthode SaveChanges de la classe DbContext, déjà utilisée à de nombreuses reprises dans la section Cycle de vie des entités.

Malgré sa simplicité apparente, SaveChanges déclenche plusieurs traitements de manière à générer et à exécuter les ordres à destination de la source de données. Le premier d’entre eux est le lancement de la détection des changements, lorsque la propriété AutoDetectChangesEnabled du Change Tracker a pour valeur true.

La génération de requêtes qui s’ensuit s’appuie sur l’état défini pour chaque entité. Lorsqu’une entité est dans l’état Added, une requête INSERT est créée. Pour l’état Modified, c’est une requête UPDATE qui est générée, et dans le cas de l’état Deleted, c’est une requête DELETE. Lorsqu’une entité est dans l’état Unchanged ou Detached, aucune opération n’est exécutée pour celle-ci.

L’exécution de l’ensemble de ces requêtes est effectuée dans le contexte d’une transaction implicite lorsque la source de données le permet. Si tout se passe bien, la transaction implicite est validée, et l’état des entités est passé à Unchanged ou Detached afin d’indiquer qu’elles reflètent l’état de la source de données.

Certains cas plus complexes peuvent toutefois se produire, notamment en ce qui concerne les suppressions. En fonction de la configuration de chaque clé étrangère, la librairie peut se retrouver dans une situation de suppression en cascade ou de mise à jour d’enregistrements dépendants. Dans ces deux cas, le traitement est effectué en amont de la suppression d’enregistrement...

Assurer l’intégrité des données

L’intégrité des données est une notion essentielle pour la totalité des sources de données, qu’elles soient relationnelles, orientées document, basées sur des fichiers texte, ou même tout simplement en mémoire. Certaines d’entre elles, les bases de données relationnelles, intègrent des mécanismes avancés dont l’objectif est uniquement d’assurer la cohérence des informations qu’elles contiennent : c’est notamment le cas des contraintes de clés étrangères. D’autres mécanismes peuvent également être impliqués pour la gestion des accès concurrents ou l’exécution d’ensembles d’instructions sous une forme atomique.

1. Accès concurrents

La problématique de l’accès en écriture à un enregistrement par plusieurs utilisateurs est récurrente dans les environnements client-serveur. Il existe trois manières de procéder pour gérer ce type de cas.

a. Loi du plus fort

Ce mode de fonctionnement représente le comportement par défaut d’Entity Framework Core. Il n’est pas à proprement parler une méthode de gestion des accès concurrents puisqu’il laisse les utilisateurs modifier librement les données avec, pour seule règle, que le dernier qui met à jour un enregistrement gagne, que l’enregistrement ait été modifié par un autre utilisateur ou non. C’est généralement ce comportement que l’on souhaite éviter par l’utilisation des contrôles d’accès concurrents optimistes ou pessimistes.

Pour montrer les effets de ce mode de fonctionnement, le code ci-après simule la mise à jour d’un même enregistrement par deux utilisateurs via deux threads exécutés en parallèle. Le contexte de données utilisé ne comporte qu’un type d’entité et est configuré pour fonctionner avec SQL Server.


public class ConcurrencyContext : DbContext 
{ 
    protected override void OnConfiguring(DbContextOptionsBuilder 
optionsBuilder) 
    { 
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;...

Visualisation du code SQL généré

Un des reproches régulièrement faits aux précédentes versions d’Entity Framework est la difficulté d’obtenir les instructions SQL générées par le moteur. Cette problématique est maintenant réglée puisque Entity Framework Core intègre un point d’entrée accessible simplement pour l’ajout d’une fonctionnalité de log. Il s’agit d’un service enregistré dans le cœur du moteur et implémentant l’interface ILoggerFactory. Il permet de renvoyer des instances d’objets implémentant l’interface ILogger, et dont le but est l’enregistrement d’informations liées au déroulement de l’exécution du code d’Entity Framework Core.

Le patron de conception Factory permet de centraliser les instanciations d’un ou de plusieurs types d’objets au sein d’une classe unique. Il est très utilisé au sein de la librairie Entity Framework Core pour tirer parti au maximum du mécanisme d’injection de dépendances.

Entre la factory et l’objet ILogger, il existe un intermédiaire : le fournisseur d’objets ILogger. Cet intermédiaire est utilisé pour pouvoir associer plusieurs types concrets implémentant ILogger à l’unique instance d’ILoggerFactory maintenue en mémoire par Entity Framework Core. Le fournisseur implémente l’interface ILoggerProvider et est enregistré dans la factory par un appel à la méthode...