Académique Documents
Professionnel Documents
Culture Documents
Architecture des SI I
Introduction 2
Historique 3
Architecture 4
Fonctionnalités 5
Approches 5
Références 31
1
Introduction
La première phase de développement d'une application est de déterminer la technologie à utiliser
pour l'implémentation (le langage de programmation) et la base de données à utiliser. La
question qui se pose dans ce cas est comment assurer le lien entre le langage de la base de
données qui est SQL et le langage de programmation que ce soit c#, vb...
Il existe bel et bien un moyen intermédiaire. Cet outil assure l’interaction entre les deux langages
de manière que la base de données puisse interpréter le code de développement et vis versa.
Qu’est-ce
ce qu’un ORM?
ORM (object-relational
relational mapping) est un outil qui sert à manipuler d'une manière automatisée les
données de l’objet vers la base de données relationnelle. L’ORM comprend trois parties
principales:
illustré ci-dessous:
Qu'est-ce
ce que Entity Framework Core?
Entity Framework Core (EFCore), comme tous les ORM(s), permet aux développeurs de
manipuler des données accessibles par des objets sans devoir se préoccuper de la base de
données et des tables.
EFCore prend en charge de nombreux fournisseurs de base de données: SQL Server, SQLite,
PostgreSQL, MySQL, Oracle DB ...
2
EFCore est destiné à être utilisé avec les applications .NET Core. Cependant, il peut également
être utilisé
tilisé avec les applications standard basées sur le framework .NET 4.5+.
Comme le montre la figure, EFCore fait partie de la couche Data. Il s'insère entre les classes de
domaine et la base
ase de données.
Historique
La première version de Entity Framework (EF v1) était incluse avec .NET Framework 3.5
Service Pack 1 et Visual Studio 2008 Service Pack 1, publiée le 11 août 2008.
Après plusieurs années et l'apparition de plusieurs versions de EF, Microsoft a décidé de rendre
.NET multiplateforme (fonctionne sous Windows, Linux et MacOS), ce qui signifie que la
prochaine version de EF serait une réécriture complète.
Le 27 juin 2016, la nouvelle version a été publiée sous le nom d'Entity Framework
Framewo Core 1.0,
avec .NET Core 1.0 et ASP.NET Core 1.0. Initialement, cette version s’appelait EF7, mais elle a
été renommée pour souligner qu'elle s'agissait d'une réécriture complète plutôt que d'une mise à
niveau incrémentielle et qu'elle ne remplace pas E
EF6.
Bien que Entity Framework Core 1.0 partage certaines similitudes conceptuelles avec les
versions précédentes de Entity Framework, il s'agit d'une toute nouvelle base de code conçue
pour être plus efficace, puissante, flexible, extensible, open source et multiplateforme et prend en
charge une nouvelle gamme de fournisseurs de base de données relationnelles et NOSQL.
La version la plus récente est Entity Framework Core 5.0.2 (EFCore 5), publiée le 12 janvier
2021.
3
EFCore 5 propose de nouvelles fonctionnalités qui ne sont pas implémentées dans EF6.
Cependant, toutes les fonctionnalités de EF6 ne sont pas actuellement iimplémentées dans
EFCore 5.
La tableau suivant illustre les dates de sortie des différentes versions de EFCore:
Architecture
● EDM (Entity Data Model): est un ensemble de concepts qui décrivent la structure des
données, quelle que soit sa forme stockée. Il se compose de trois parties principales:
1. Modèle conceptuel: contient les entités et leurs relations.
2. Modèle de stockage: est le modèle de conception de base de données qui contient
des tables, vues, procédures stockées, ainsi que leurs relations et les clés.
4
3. Mapping: contient des informations sur la façon dont le modèle conceptuel est
mappé sur le modèle de stockage.
● LINQ to Entities: permet aux développeurs d'écrire des requêtes par rapport au modèle
conceptuel Entity Framework à l'aide du langage Visual Basic ou C#.
● EntitySQL: est un langage similaire à SQL qui vous permet d'interroger des entités dans
Entity Framework.
● Object Service: est un composant d'Entity Framework qui vous permet d’insérer, mettre
à jour et supprimer des données exprimées sous forme d'objets. Ce composant prend en
charge les requêtes LINQ (Language-Integrated Query) et les requêtes Entity SQL par
rapport aux types définis dans un modèle EDM (Modèle de données d'entité).
● Entity Client Data Provider: est un fournisseur de données utilisé par Entity
Framework. EntityClient utilise d’autres fournisseurs de données telles que le .NET
Framework pour accéder à la source de données. Par exemple, EntityClient utilise le
fournisseur de données .NET Framework pour SQL Server (SqlClient) pour accéder à la
base de données SQL Server.
● ADO.Net Data Provider: Cette couche communique avec la base de données en
utilisant ADO.Net standard.
Fonctionnalités
L'API EFCore inclut les fonctionnalités suivantes:
Approches
Il existe deux approches de l’EFCore : Code First et Database First
5
Si vous avez déjà une base de données, EF permet de générer automatiquement un modèle dde
données (.edmx) qui contient des classes et des propriétés qui correspondent à des objets de la
Vous avez une base de données existante ou non, vous pouvez coder vos propres classes et
propriétés qui correspondent aux tables et colonnes et de les utiliser avec EF, sans fichier. Edmx.
EF peut également créer la base de données à partir des entités.
Le principe est de générer la base à partir d’un traitement de code. Pour se faire on à besoin
d’écrire les entités, de définir leurs propriétés de navigation, développer la classe context et
déclarer les entités qui vont être des tables en tant que DbSet. Le Mapping des classes dans la
base de données sera fait d’une manière implicite en utilisant les conventions par défaut de
EFCore CodeFirst. On peut également configurer les entités en utilisant Data Annotation ou
Fluent API afin de remplacer les conventions par défaut.
Nous allons détailler toutes les étapes à travers un exemple. Dans cet exemple nous allons suivre
l’architecture suivante:
6
● Création de la solution et installation de EF Core
La première étape à faire est la création de la solution qui contient les 3 couches ; deux projets de
type bibliothèque de classes (CodeFirst.Data et CodeFirst.Domain) et un projet de type
application console (CodeFirst.Console).
EFCore ne fait pas partie de .NET Core. Il est disponible sous forme de package NuGet.
7
● Création du modèle de données dans le projet Domain
La deuxième étape est la création des entités et leurs propriétés dans la couche Domain. Prenons
l’exemple suivant d’une application de gestio
gestion de compte bancaire.
namespace CodeFirst.Domain
{
public class Agence
{
public int AgenceId { get; set; }
public string Nom { get; set; }
public int NombreEmploye { get; set; }
public virtual ICollection<Compte> comptes { get; set; }
}
namespace CodeFirst.Domain
{
public class Compte
{
public int CompteId { get; set; }
public int RIB { get; set;; }
public virtual Agence Agence { get; set; }
}
}
8
● Implémentation du context dans le projet Data
Une fois les entités sont créées. L’approche Code First exige aussi la classe context dérivée de
DbContext. DbContext est une partie importante de l’EFCore. C’est la classe qui est responsable
de l'interaction avec la base de données.
La classe context contient les DbSet qui représentent les entités qui seront par la suite
transformées en tables. Elle redéfinit également les méthodes OnConfiguring et
OnModelCreating. On doit créer une instance de la classe context pour se connecter à la base de
données et enregistrer ou récupérer les données.
Chaque DbContext instance doit être configurée pour utiliser un et un seul fournisseur de base de
données. (Les différentes instances d’un DbContext sous-type peuvent être utilisées avec
différents fournisseurs de bases de données, mais une seule instance ne doit en utiliser qu’une
seule.) Un fournisseur de base de données est configuré à l’aide d’un Use* appel spécifique.
Dans notre cas, on va utiliser le fournisseur de base de données SQL Server. Ces Use* méthodes
sont des méthodes d’extension implémentées par le fournisseur de base de données.
Afin que la méthode d’extension Use* puisse être utilisée, le package NuGet du fournisseur de
base de données (SQL Server) doit être installé.
La chaîne de connexion dans la méthode UseSqlServer fournit des informations sur la base de
données. DataSource spécifie le serveur de base de données à utiliser, InitialCatalog spécifie le
nom de la base de données à créer et IntegratedSecurity spécifie le mode d'authentification
Windows. EFCore utilisera cette chaîne de connexion lors de la génération de la base de
données.
9
DbSet est une collection d’entités, donc généralement va prendre le nom de l’entité au pluriel.
Chaque DbSet représente une image d'une table, par exemple [DbSet<Compte> Comptes]
représente la table Comptes.
namespace CodeFirst.Data
{
public class GestionCompteContext : DbContext
{
public GestionCompteContext()
{
}
public DbSet<Agence> Agences { get; set; }
public DbSet<Compte> Comptes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\mssqllocaldb;Initial
Catalog=GestionCompteDB;Integrated Security=true");
base.OnConfiguring(optionsBuilder);
}
}
}
● Migrations
Après avoir créé les classes des entités et la classe context, il est temps d’utiliser les migrations
pour générer la base de données.
Afin de pouvoir utiliser les migrations on doit installer les deux packages NuGet suivants:
EFCore CodeFirst dispose de deux commandes pour créer et appliquer une migration:
10
● Add-Migration:: pour enregistrer les modifications que vous avez apportées à vos
entités.
● Update-Database:: pour appliquer les modifications en attente à la base de données
basée sur la dernière version créée avec la commande Add-Migration
Migration.
Dans Visual Studio, ouvrez la console du gestionnaire de packages NuGet depuis Outils ->
Gestionnaire de packages NuGet -> Console du gestionnaire de packages.
11
qui contient des instructions pour la création et une autre méthode Down() pour la
suppression.
EFCore CodeFirst a créé la base de données avec le nom et l'emplacement spécifiés dans la
chaînee de connexion dans la méthode UseSqlServer(). Il a créé une table pour chaque propriété
DbSet (Agences et Comptes) et chaque table contient des colonnes avec le type de donnée et la
longueur appropriée. Le nom des colonnes et le type des données correspondent
correspon aux propriétés
des classes d’entités. Comme le montre la figure suivante, les colonnes CompteId et AgenceId
sont des clés primaires et la colonne AgencesAgenceId est une clé étrangère.
Il s'agissait de la première migration pour créer une base de données. Désormais, chaque fois
qu'on ajoute ou on met à jour des classes ou des configurations de domaine, on doit synchroniser
la base de données avec le modèle à l'aide des commandes Add-Migration et Update-Database.
12
3. Evolution de modèle
EFCore utilise les migrations afin de pouvoir mettre à jour automatiquement le schéma de la
base de données lorsque le modèle change, sans perdre toutes les données existantes ou d’autres
objets de la base de données.
Après la création de la base de données, on a décidé d’ajouter une nouvelle classe d’entité.
13
⮚ Ajouter la classe Client dans le projet Domain.
namespace CodeFirst.Domain
{
public class Client
{
public int ClientId { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public virtual ICollection<Compte> Compte { get; set; }
}
}
Le modèle et la base de données de données ne sont plus synchronisés. Afin d’apporter les
modifications nécessaires à la base de données on doit créer une nouvelle migration.
Il est préférable de donner aux migrations un nom descriptif pour faciliter par la suite la
compréhension de l’historique du projet.
Cette fois, EF détecte que la base de données existe déjà. Par ailleurs, lors de l’application de la
première migration, elle a été enregistrée dans une table d’historique des migrations spéciale
dans la base de données. Ainsi, EF peut appliquer automatiquement la nouvelle migration
uniquement.
14
4. Suppression d’une migration
Parfois, on ajoute une migration et on réalise qu’on doit apporter des modifications
supplémentaires au modèle avant de l’appliquer. Pour supprimer la dernière migration, on utilise
la commande: Remove-Migration
Si on veut restaurer le schéma de la base de données à l'un des états précédents, on peut alors
utiliser la commande Update-DataBase avec le paramètre -Migration suivi du nom de la
migration.
Supposons qu’après l’application d’une première migration nommée MyFirstMigration, on a
modifié le modèle et on a créé une deuxième migration nommée MySecondMigration et on a
appliqué cette migration à la base de données à l'aide de la commande Update-Database. Mais,
pour une raison quelconque, on souhaite rétablir l'état précédent de la base de données. Dans ce
cas, on utilise la commande Update-Database -Migration MyFirstMigration pour rétablir la base
de données sur l'instantané de la première migration.
Les méthodes EnsureCreated et EnsureDeleted fournissent une alternative légère aux migrations
pour la gestion du schéma de base de données. Ces méthodes sont utiles dans les scénarios où les
données sont temporaires et peuvent être supprimées lorsque le schéma est modifié. Par exemple
pendant le prototypage, les tests ou les caches locaux.
Certains fournisseurs (surtout non relationnels) ne prennent pas en charge les migrations. Pour
ces fournisseurs, EnsureCreated est souvent le moyen le plus simple d’initialiser le schéma de
base de données.
EnsureCreated et les migrations ne fonctionnent pas correctement ensemble. Si vous utilisez des
migrations, n’utilisez pas EnsureCreated pour initialiser le schéma.
La transition de EnsureCreated à des migrations n’est pas une expérience transparente. La façon
la plus simple de procéder consiste à supprimer la base de données et à la recréer à l’aide des
migrations. Si vous prévoyez d’utiliser des migrations à l’avenir, il est préférable de commencer
par les migrations au lieu d’utiliser EnsureCreated.
La méthode EnsureDeleted supprime la base de données, le cas échéant. Si vous ne disposez pas
des autorisations appropriées, une exception est levée.
15
● Conventions par défaut
Les conventions sont des règles par défaut à l'aide desquelles EFCore crée un schéma de base de
données basé sur les classes de domaine et de context sans aucune configuration supplémentaire.
Par Convention, les types d’entités qui sont exposés dans les propriétés DbSet de la classe
context sont inclus dans le modèle en tant qu’entités. Les types d’entités trouvés dans les
propriétés de navigation d’autres types d’entités sont également inclus ainsi que les types
d’entités qui sont spécifiés dans la méthode OnModelCreating.
namespace CodeFirst.Domain
{
public class Credit
{
public int CreditId { get; set; }
public float Somme { get; set; }
public DateTime DateCredit { get; set; }
public float TauxInteret { get; set; }
public virtual Compte Comptes { get; set; }
}
}
16
EFCore a ajouté la nouvelle table Credit dans la base de données, car il a découvert le type
Par Convention, chaque type d’entité sera configuré pour être mappé à une table de base de
données portant le même nom que la propriété DbSet qui expose l’entité. S’il n’existe aucun
DbSet pour l’entité donnée, le nom de la classe est utilisé.
Chaque type d’entité dans le modèle a un ensemble de propriétés. Dans une base de données
relationnelle, les propriétés d’entité sont mappées à des colonnes de table.
Par Convention, toutes les propriétés publiques avec un accesseur
accesseur get et un accesseur Set seront
incluses dans le modèle.
Par Convention, lors de l’utilisation d’une base de données relationnelle, les propriétés d’entité
sont mappées à des colonnes de table portant le même nom que la propriété.
Par exemple, SQL Server mappe les DateTime Propriétés aux datetime2(7) colonnes et string
aux propriétés nvarchar(max) des colonnes (ou à nvarchar(450) pour les propriétés utilisées
comme clé).
17
Une propriété est considérée comme facultative si elle est valide pour contenir null . Si null n’est
pas une valeur valide à assigner à une propriété, elle est considérée comme étant une propriété
obligatoire. Lors du mappage à un schéma de base de données relationnelle, les propriétés
requises sont créées en tant que colonnes n’acceptant pas les valeurs NULL, et les propriétés
facultatives sont créées en tant que colonnes Nullable.
Par Convention, une propriété dont le type .NET peut contenir une valeur null sera configurée
comme étant facultative, alors que les propriétés dont le type .NET ne peut pas contenir de valeur
null seront configurées selon les besoins. Par exemple, toutes les propriétés avec des types valeur
.net ( int , decimal , bool , etc.) sont configurées en fonction des besoins, et toutes les propriétés
avec des types valeur .net Nullable ( int? , decimal? ,, bool? etc.) sont configurées comme étant
facultatives.
C# 8 a introduit une nouvelle fonctionnalité appelée types de référence Nullable (Diagnostics
proactifs NRT), qui permet d’annoter des types de référence, indiquant s’il est valide qu’ils
contiennent ou non des valeurs NULL. Cette fonctionnalité est désactivée par défaut et affecte le
comportement de EFCore de la façon suivante :
● Si les types de référence Nullable sont désactivés (valeur par défaut), toutes les
propriétés avec des types de référence .NET sont configurées comme étant
facultatives par convention (par exemple, string ).
● Si les types de référence Nullable sont activés, les propriétés seront configurées en
fonction de la possibilité de valeur null C# de leur type .NET : string? sera
configuré comme étant facultatif, mais string sera configuré comme requis.
Par Convention, une propriété nommée Id ou <type name>Id sera configurée comme clé
primaire d’une entité.
Vous pouvez également configurer plusieurs propriétés comme clé d’une entité. Il s'agit d’une
clé composite.
18
Par Convention, sur les bases de données relationnelles, les clés primaires sont créées avec le
Si EFCore prend en charge l’utilisation de propriétés de n’importe quel type primitif comme clé
primaire, y compris string , Guid byte[] et d’autres, toutes les bases de données ne prennent pas
en charge tous les types en tant que clés. Dans certains cas, les valeurs de clé peuvent être
converties automatiquement en un type pris en charge ; sinon, la conversion doit être spécifiée
manuellement.
16. Relations
Une relation définit la relation entre deux entités. Dans une base de données relationnelle, elle est
représenteé par une contrainte de clé étrangère.
Par défaut, une relation est créée lorsqu’une propriété de navigation est détectée sur un type. Une
propriété
iété est considérée comme une propriété de navigation si le type vers lequel elle pointe ne
peut pas être mappé en tant que type scalaire par le fournisseur de base de données actuel.
Le modèle le plus courant pour les relations est d’avoir des propriétés de navigation définies aux
deux extrémités de la relation et une propriété de clé étrangère définie dans la classe d’entité
dépendante.
Bien qu’il soit recommandé d’avoir une propriété de clé étrangère définie dans la classe d’entité
dépendante, elle n’est pas obligatoire. Si aucune propriété de clé étrangère n’est trouvée, une
propriété de clé étrangère Shadow est introduite avec le nom <navigation property
name><principal key property name> ou <principal entity name><principal key property name>
si aucune navigation n’est présente sur le type dépendant.
19
L’inclusion
’inclusion d’une seule propriété de navigation (aucune navigation inverse et aucune propriété
de clé étrangère) suffit à avoir une relation définie par Convention. Vous pouvez également avoir
Par Convention, la suppression en cascade sera définie sur cascade pour les relations requises et
ClientSetNull pour les relations facultatives. Cascade signifie que les entités dépendantes sont
également supprimées. ClientSetNull
ClientSetNull signifie que les entités dépendantes qui ne sont pas
chargées en mémoire restent inchangées et doivent être supprimées manuellement ou mises à
jour pour pointer vers une entité principale valide. Pour les entités chargées en mémoire, EFCore
tente de définir
éfinir les propriétés de clé étrangère sur la valeur null.
● Annotations
Parfois, les conventions par défaut ne sont pas idéales pour votre modèle, vous devez donc
configurer les entités individuellement en utilisant les annotations ou les configuration
FluentApi.
DataAnnotation est une configuration basée sur un simple attribut,
attribut, on peut l’appliquer sur les
classes et leurs propriétés. Les attributs existent dans les deux namespaces suivants:
● System.ComponentModel.DataAnnotations
● System.ComponentModel.DataAnnotations.Schema
20
1. System.ComponentModel.DataAnnotations
Fournit des classes d’attributs utilisées pour définir des métadonnées pour les contrôles de
données ASP.NET et ASP.NET MVC.
Classes:
AssociatedMetadataTypeTypeD Étend les informations de métadonnées pour une classe en
escriptionProvider ajoutant les informations d'attributs et de propriétés
définies dans une classe associée.
21
DisplayAttribute Fournit un attribut à usage général qui vous permet de
spécifier les chaînes localisables pour les types et membres
de classes d'entité partielles.
22
MaxLengthAttribute Spécifie la longueur maximale du tableau ou des données
de type chaîne autorisée dans une propriété.
23
StringLengthAttribute Spécifie les longueurs minimale et maximale de caractères
autorisées dans un champ de données.
24
Validator Définit une classe d'assistance qui peut être utilisée pour
valider des objets, des propriétés et des méthodes
lorsqu'elle est incluse dans leurs attributs
ValidationAttribute associés.
Interfaces:
Énumérations:
2. System.ComponentModel.DataAnnotations.Schema
Fournit la prise en charge des classes d’attributs utilisées pour définir des métadonnées pour les
contrôles de données ASP.NET et ASP.NET MVC.
Classes:
25
Dénote que la classe est un type complexe. Les types
ComplexTypeAttribute complexes sont les propriétés non scalaires des types
d'entités qui permettent d'organiser les propriétés scalaires
au sein des entités. Les types complexes n’ont pas de clés
et ne peuvent pas être gérés par l’Entity Framework, mis à
part l’objet parent.
Énumérations:
26
using System.ComponentModel.DataAnnotations;
namespace CodeFirst.Domain
{
public class Agence
{
[Key]
public int AgenceId { get; set; }
[DataType(DataType.ImageUrl)]
public string Image { get; set; }
[Range(0, int.MaxValue)]
public int NombreEmploye { get; set; }
L'API Fluent fournit des méthodes pour configurer divers aspects d’un modèle de données:
● Configuration du modèle
● Configuration des entitées
● Configuration des propriétés
Le tableau suivant liste quelques méthodes importantes pour chaque type de configuration.
27
Configuration du modèle:
28
HasDiscriminator Configure la propriété de discriminateur utilisée pour
identifier le type d’entité.
29
● Types d’entité détenus
EFCore permet de modéliser des types d'entités qui peuvent uniquement apparaître dans les
propriétés de navigation d’autres types d’entités. Il s’agit de types d’entités détenues. L’entité
contenant un type détenu est son propriétaire.
Les entités détenues sont essentiellement une partie de l’entité propriétaire et ne peuvent pas
exister sans elle.
Les types détenus ne sont pas inclus dans le modèle selon les conventions par défaut de EFCore.
1. Annoter le type avec l’attribut Owned pour configurer le type en tant que type détenu.
2. Utiliser la OwnsOne méthode dans OnModelCreating. OnModelCreating est une
méthode de la classe context qu’on peut substituer afin de configurer davantage le
modèle qui a été découvert par convention à partir des types d'entités exposés dans les
DbSet.
● Stratégies d’héritage
Par convention, EFCore ne recherche pas automatiquement les types de base ou dérivés.
Cela signifie que si vous souhaitez qu’un type de votre hiérarchie soit mappé, vous devez
spécifier explicitement ce type sur votre modèle. Par exemple, si vous spécifiez uniquement le
type de base d’une hiérarchie, EFCore n’inclura pas tous ses sous-types de manière implicite.
2. Configuration TPT
La fonctionnalité table par type (TPT) a été introduite dans EFCore 5.0. Cette approche suggère
une table séparée pour chaque classe de la hiérarchie d'héritage.
30
1. Le chargement hâtif signifie que les données associées sont chargées à partir de la base
de données dans le cadre de la requête initiale.
2. Le chargement explicite signifie que les données associées sont chargées explicitement
à partir de la base de données à un moment ultérieur.
3. Le chargement différé signifie que les données associées sont chargées de façon
transparente à partir de la base de données lors de l’accès à la propriété de navigation.
Dans ce cours on va se focaliser seulement sur le chargement différé avec des proxies.
EF Core active ensuite le chargement différé pour n’importe quelle propriété de navigation qui
peut être substituée, c’est-à-dire qui doit être virtual et sur une classe qui peut être héritée.
Références
[1] https://docs.microsoft.com/en-us/ef/core/
[2] https://www.entityframeworktutorial.net/efcore/entity-framework-core.aspx
[3] https://en.wikipedia.org/wiki/Entity_Framework
31