logo blog it atawiz
chevron up
9 minutes de lecture
Nouveautés EF 8 : Les types complexes (Complex Types)
Sengdao - il y a un mois
Découvrez l'une des nouvelles features d’Entity Framework 8 : utiliser et stocker des valeurs objet via les types complexes dans une base de données

Table of contents

Introduction

Qu’est-ce que les types complexes ?

Comment utiliser les types complexes dans Entity Framework 8

Configuration au niveau du DbContext

Avantages des types complexes

Conclusion

Introduction

.NET 8 et EF 8 sont enfin disponibles ! Dans cet article, nous parlerons d’une des nouvelles features d’Entity Framework 8 : utiliser et stocker des valeurs objet (object values) via les types complexes (Complex Types) dans une base de données.

Qu’est-ce que les types complexes ?

Aujourd’hui on peut catégoriser les objets que l’on sauvegarde en base de données selon 3 types :

  • Les types qui ne contiennent qu’une valeur unique : les types « primitifs » (ex : int, string)
  • Les types qui contiennent plusieurs valeurs avec une clé d’identification unique : les types « entités » (ex : User, Customer)
  • Les types qui peuvent contenir plusieurs valeurs mais ne possèdent pas de clé d’identification (ex : Coordinate)

Avant EF 8 il n’était pas possible de mapper facilement le dernier type de données. Nous nommerons ce dernier « type complexe ».

Ce type complexe est défini par les critères suivants:

  • Il n’est ni tracké ni identifié par une clé

  • Il doit être défini comme faisant partie d’un autre type (Impossible de créer de DbSet avec ce type)

  • Il doit être soit un type référence, soit un types valeur

  • Ses instances peuvent être partagées entre différentes entités

Si nous souhaitons le ramener à un cas plus fonctionnel, nous l’utiliserons pour modéliser un objet qui fait partie d’une entité comme des adresses, des coordonnées ou des numéros de téléphone.

Comment utiliser les types complexes dans Entity Framework 8

Prenons l’exemple suivant : je veux pouvoir stocker des Coordinate dans 2 entités.

  • PointOfInterest
  • Route

Je me retrouve donc avec les 3 classes suivantes.

public class Coordinate 
{ 
    public required double Latitude { get; set; } 
    public required double Longitude { get; set; } 
}
public class PointOfInterest 
{ 
   public int Id { get; set; } 
   public required string Name { get; set; } 
   public required string Type { get; set; } 
   public required string Description { get; set; } 
   public required Coordinate Location { get; set; } 
}
public class Route 
{ 
    public int Id { get; set; } 
    public required string Name { get; set; } 
    public required string Description { get; set; } 
    public required Coordinate Origin { get; set; } 
    public required Coordinate Destination { get; set; } 
} 

Configuration au niveau du DbContext

On peut déclarer notre type complexe de deux manières :

  • Soit avec l’attribut ComplexTypeAttribute
[ComplexType] 
public class Coordinate 
{ 
    public required double Latitude { get; set; } 
    public required double Longitude { get; set; } 
} 
  • Soit dans la surcharge de la méthode OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<PointOfInterest>() 
        .ComplexProperty(e => e.Location); 
    modelBuilder.Entity<Route>(b => 
    { 
        b.ComplexProperty(e => e.Origin); 
        b.ComplexProperty(e => e.Destination); 
    }); 
}

Maintenant je veux sauvegarder une instance de PointOfInterest avec ses coordonnées GPS.

PointOfInterest poi = new() { Name = "Atawiz", Description = "Pure players Microsoft", Type = "ESN", Location = new() { Latitude = 48.87338449053729, Longitude = 2.3348333246787263 } }; 
_dbContext.Add(poi); 
await _dbContext.SaveChangesAsync(); 

Ce code est traduit de la manière suivante en SQL.

INSERT INTO [PointOfInterests] ([Name], [Description], [Type], [Location_Latitude], [Location_Longitude]) 
OUTPUT INSERTED.[Id] 
VALUES (@p0, @p1, @p2, @p3, @p4); 

Avantages des types complexes

Les principaux avantages sont les suivants :

  • L’encapsulation de plusieurs propriétés dans un seul et unique type complexe : Dans notre exemple, Latitude et Longitude sont une paire et nous allons rarement les voir séparés. Il semble alors tout à fait pertinent de les regrouper dans une classe utilisée en type complexe.

  • La capacité de réutilisation : Cet avantage découle en partie du premier : le fait d’encapsuler des propriétés dans une même classe. On peut facilement ajouter ces propriétés à d’autres entités comme on l’a fait avec l’entité Route dans nos classes.
    Si jamais une nouvelle propriété Altitude était introduite dans notre type complexe Coordinate, elle serait ajoutée automatiquement à nos deux entités.

  • Simplicité d’utilisation : On peut facilement mapper des classes de types complexes via l’attribut [ComplexType] ou dans la surcharge de OnModelCreating avec la méthode .ComplexProperty().

Limitations des types complexes

Bien évidemment la fonctionnalité n’est pas parfaite et présente quelques limitations :

  • Pas de clé / doit faire partie d’une entité : C’est un choix de conception, les types complexes ont été introduits pour permettre de grouper des propriétés d’une entité dans une classe réutilisable. On ne peut pas créer de DbSet à partir d’un type complexe

  • Les types complexes peuvent à la fois être de type référence ou de type valeur

  • Instances partagées : Une même instance de type complexe peut être utilisée pour plusieurs propriétés. Par exemple si l’on avait utilisé la même instance pour Origin et Destination dans notre entité Route, une modification d’Origin aurait entraîné la modification de Destination. Cela est entre autres dû au fait que par défaut un type complexe est de type référence.

Conclusion

La compatibilité avec les types complexes est donc une nouvelle fonctionnalité assez sympa que nous apporte Entity Framework 8. On catégoriserait ses apports comme étant du nice-to-have, car ses cas d’usage sont assez spécifiques. Il faut veiller à ne pas en abuser car cela pourrait avoir un impact sur la complexité du code et potentiellement des performances.

Pour en savoir plus, voici le lien vers la documentation officielle de Microsoft.

https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/whatsnew https://devblogs.microsoft.com/dotnet/announcing-ef8-rc1/