Blog ENI : Toute la veille numérique !
-25€ dès 75€ sur les livres en ligne, vidéos... avec le 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. Modélisation avancée
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

Modélisation avancée

Séquences

Les séquences sont des objets intégrés à certains moteurs de bases de données qui ont pour seul et unique objectif de générer et de renvoyer à la demande des valeurs numériques séquentielles. Elles ne sont pas associées à une table particulière et peuvent donc être utilisées par plusieurs tables, procédures stockées ou fonctions de base de données.

Entity Framework supporte leur utilisation au sein de bases de données relationnelles, dans le cadre d’une approche de création de base de données à l’aide de migrations. La configuration d’une séquence est différente de tout ce que nous avons pu voir au chapitre Modélisation par le fait que les séquences sont associées directement à un modèle objet, par le biais de la méthode ModelBuilder.HasSequence. Cette fonction attend au minimum une valeur pour son paramètre name, qui définit le nom de la séquence au niveau de la source de données.


public class SequencesContext : DbContext 
{ 
    protected override void OnConfiguring(DbContextOptionsBuilder 
optionsBuilder) 
    { 
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;
Database=SequencesDb;Trusted_Connection=True;"); 
    } 
 
    protected override void OnModelCreating(ModelBuilder 
modelBuilder) 
    { 
        modelBuilder.HasSequence("Sequence_DbRecordNumber"); 
    } 
}
 

L’instruction SQL générée pour créer la séquence dans le cas présent, à savoir avec une source de données SQL Server, est la suivante :


CREATE SEQUENCE [Sequence_DbRecordNumber]  
START WITH 1  
INCREMENT BY 1  
NO MINVALUE  
NO MAXVALUE  
NO CYCLE;
 

L’instruction CREATE SEQUENCE n’existe pour SQL Server que depuis sa version 2012. Son exécution échouera donc si la version du moteur de base de données est antérieure.

Cinq paramètres sont définis dans cette instruction de création. Ils peuvent tous être configurés au sein du contexte...

Héritage

L’héritage est une des composantes majeures de la programmation orientée objet, et rares sont les applications d’envergure qui n’en tirent pas parti explicitement. Tout type d’une application peut être impliqué dans une relation d’héritage, et cette assertion est également valable pour les classes représentant les enregistrements issus de sources de données externes. Les fournisseurs de données relationnels d’Entity Framework Core permettent de tirer parti de cette structuration par l’intégration de la stratégie nommée Table Per Hierarchy (TPH).

Il est à noter que la gestion de l’héritage est de la responsabilité des fournisseurs de données. Ainsi, un fournisseur tiers ne se basant pas sur l’assembly Microsoft.EntityFrameworkCore.Relational pourra implémenter cette fonctionnalité différemment.

Ce mode de gestion de l’héritage au niveau d’une source de données repose sur la création de tables qui incluent l’ensemble des colonnes associées aux types de l’arborescence hiérarchique. Le type de chacun des enregistrements contenus dans cette table est fixé à l’aide d’une colonne supplémentaire contenant une valeur discriminante qui identifie le type en question.

La mise en place de la stratégie...

Utilisation des procédures stockées

Entity Framework, dans ses versions précédentes, proposait d’effectuer un mappage entre des procédures stockées et des méthodes .NET. Entity Framework ne propose pas à ce jour cette fonctionnalité, mais l’équipe en charge du développement de la librairie assure qu’elle est actuellement dans la liste des ajouts prévus. Il existe bien évidemment une solution de contournement, satisfaisante dans un cas, beaucoup moins pour les autres : l’utilisation de SQL.

Lorsque la procédure renvoie une donnée qui correspond à un type d’entité, l’utilisation de la méthode FromSql fait des merveilles.

Le script SQL suivant crée une procédure stockée simpliste pour la base de données Northwind.


CREATE PROCEDURE CustomersByCountry  
    @country  nvarchar(15)  
AS 
SELECT * FROM Customers 
WHERE Country = @country
 

L’exemple suivant exécute la procédure stockée nouvellement créée et retourne une collection d’objets de type Customers sans difficulté.


using (var context = new NorthwindContext()) 
{ 
    var clients = context.Customers 
                .FromSql("EXECUTE CustomersByCountry {0}", "France") 
                .ToList(); 
 
    foreach (var client in clients) 
    { 
    ...

Shadow properties

Les shadow properties (propriétés de l’ombre, en français) sont des propriétés associées au modèle géré par Entity Framework, mais qui ne possèdent pas d’implémentation au niveau du type auquel elles sont associées. Elles sont maintenues par le Change Tracker, qui conserve les valeurs qui leur sont associées ainsi que leur état.

1. Utilisation au cœur d’Entity Framework

L’utilisation des shadow properties peut se révéler particulièrement puissante puisqu’elles permettent l’écriture de code générique, applicable à n’importe quelle entité respectant une condition particulière. Ce comportement est d’ailleurs utilisé au sein même d’Entity Framework Core, au niveau des conventions associées aux relations entre entités. En effet, lorsqu’une relation est découverte à la création du modèle mais qu’aucune propriété définie comme clé étrangère correspondante n’est trouvée au niveau de l’entité dépendante, Entity Framework enregistre une shadow property destinée à remplir ce rôle. L’exemple suivant montre un cas concret de modèle objet dans lequel cette convention est appliquée.


public class Context : DbContext 
{ 
    protected override void OnConfiguring(DbContextOptionsBuilder 
optionsBuilder) 
    { 
        base.OnConfiguring(optionsBuilder); 
 
        string connectionString = @"Server=(localdb)\mssqllocaldb;
Database=EFCore.ShadowProperties;Trusted_Connection=True;"; 
        optionsBuilder.UseSqlServer(connectionString); 
    } 
 
    public DbSet<Personne> Personnes { get; set; } 
 
    public DbSet<Pays> Pays { get; set; } 
 
} 
 
public class Personne 
{ 
    public int Id { get; set; } 
 
    public string Nom { get; set; } 
 
    public string Prenom { get; set; } 
 
    public string Ville...

Backing fields

Les backing fields (ou champs de stockage) sont les variables membres d’un type qui sont les véritables conteneurs de données. Les propriétés ne sont que des passerelles vers les valeurs que les backing fields contiennent.

Entity Framework permet, depuis sa version 1.1.0, d’accéder directement aux champs de stockage plutôt que de passer par les propriétés. Ce cas d’utilisation, plutôt rare, peut toutefois s’avérer particulièrement utile puisqu’il permet par exemple d’éviter l’exécution du code présent dans l’accesseur set lors de l’étape de matérialisation de l’entité. Ce code pouvant notamment contenir des instructions de déclenchement d’événements ou de modification d’autres propriétés, il peut en effet être souhaitable d’éviter son exécution.

L’exemple suivant définit le type d’entité Employe. Lorsqu’une valeur est assignée à sa propriété MontantVentes, un événement est déclenché. Ce traitement est sans effet lors de la matérialisation de l’entité, puisque à ce stade aucun gestionnaire d’événement n’est associé à l’événement Employe.PropertyChanged. Il est donc inutile et devrait être évité.


public class BackingFieldsContext : DbContext 
{ 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
    { 
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;
Database=BackingFieldsDb;Trusted_Connection=True;"); 
    } 
 
    public DbSet<Employe> Employes { get; set; } 
} 
 
public class Employe : InotifyPropertyChanged 
{ 
    public int Id { get; set; } 
 
    public string Nom { get; set; } 
 
    public string Prenom { get; set; } 
 
    private decimal _montantDesVentes; 
    public decimal MontantVentes 
    { 
        get { return...