continuous-integration/drone/push Build is failing
Details
|
2 years ago | |
---|---|---|
.. | ||
readme_files | 5 years ago | |
CarnetDeSante.cs | 5 years ago | |
Nounours.cs | 5 years ago | |
NounoursDBEntities.cs | 5 years ago | |
Program.cs | 5 years ago | |
ReadMe.md | 5 years ago | |
StubbedContext.cs | 5 years ago | |
ex_042_012_OneToOne_conventions.csproj | 2 years ago |
ReadMe.md
ex_042_012_OneToOne_conventions
20/01/2020 ⋅ Marc Chevaldonné
Cet exemple montre comment réaliser une relation One To One entre deux entités avec Entity Framework Core et l'annotation de données.
Une version équivalente réalisée avec la Fluent API est disponible dans l'exemple
ex_042_013 : One To One with Fluent API
Comment est construit cet exemple ?
- Le projet est de type .NET Core
- Il contient quatre classes :
Nounours
CarnetDeSante
NounoursDBEntities
StubbedContext
Les classes entités : Nounours
et CarnetDeSante
Un Nounours
contient différentes propriétés (Nom
, DateDeNaissance
, NbPoils
) dont Carnet
de type CarnetDeSante
.
Un CarnetDeSante
possède une propriété de type DateTime
(LastModified
), et une propriété Owner
de type Nounours
.
On a donc bien une relation One To One puisqu'un Nounours
possède un CarnetDeSante
et qu'un CarnetDeSante
possède un Nounours
.
Ce qu'il faut noter :
Nounours
possède une association versCarnetDeSante
public CarnetDeSante Carnet { get; set; }
CarnetDeSante
possède une association versNounours
public Nounours Owner { get; set; }
Nounours
possède un identifiant unique (qui peut être utilisé par convention d'écriture ou annotation de données) :[Key]
.- Cet identifiant est généré par la base :
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
. CarnetDeSante
possède un identifiant unique (qui peut être utilisé par convention d'écriture ou annotation de données) :[Key]
.- Cet identifiant fait référence à une clé étrangère qui est la clé primaire associée à l'entité pointée par sa propriété
Owner
:ForeignKey("Owner")
.
[Key, ForeignKey("Owner")]
public int UniqueId
{
get; set;
}
- En conséquence, l'identifiant unique
UniqueId
deCarnetDeSante
aura la même valeur que l'UniqueId
d'une entitéNounours
.
La classe NounoursDBEntities
- Comme dans les exemples précédents,
NounoursDBEntities
dérive deDbContext
. NounoursDBEntities
déclare deuxDbSet
: un deNounours
et l'autre deCarnetDeSante
.
public DbSet<Nounours> NounoursSet { get; set; }
public DbSet<CarnetDeSante> Carnets { get; set; }
Quelques explications supplémentaires :
Pour pouvoir relier une entité Nounours
à une entité CarnetDeSante
de manière bidirectionnelle, les propriétés Nounours.Carnet
et CarnetDeSante.Owner
sont suffisantes, à condition qu'une clé étrangère soit déclarée et utilisée.
C'est le cas grâce à l'annotation de données ForeignKey("Owner")
qui s'occupe de définir de quelle manière les deux entités sont reliées.
La classe StubbedContext
StubbedContext
est une classe fille deNounoursDBEntities
. Son rôle est de proposer un Stub en plus de ce que sait déjà faire sa classe mère. Elle ne sera donc utilisée que pour des tests unitaires ou fonctionnels. En conséquence, elle reprend tout ce que fait sa classe mère et ne change que la méthodeOnModelCreating
qui appelle la méthode de la classe mère puis ajoute des instances d'entités, grâce à la méthode d'extensionHasData
.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CarnetDeSante>().HasData(
new CarnetDeSante { UniqueId=1, LastModified = DateTime.Today },
new CarnetDeSante { UniqueId=2, LastModified = new DateTime(1980, 5, 21) },
new CarnetDeSante { UniqueId=3, LastModified = new DateTime(1983, 5, 25) }
);
modelBuilder.Entity<Nounours>().HasData(
new Nounours { UniqueId=1, Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 },
new Nounours { UniqueId=2, Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 },
new Nounours { UniqueId=3, Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }
);
}
- Remarquez que À AUCUN MOMENT nous ne précisons les valeurs des propriétés
Owner
deCarnetDeSante
etCarnet
deNounours
. Le simple fait d'utiliser la même clé (propriétésUniqueId
) est suffisant puisque celle deCarnetDeSante
est une clé étrangère. - Notez que ce ne sera pas le cas lors d'une utilisation classique de nos classes (ajout, modification...). Nous ne donnerons plus les identifiants directement mais les références des propriétés
Carnet
etOwner
.
La classe Program
- La classe
StubbedContext
est ensuite indirectement utilisée dansProgram
pour remplir la base de manière tout à fait classique et ne nécessite aucun appel supplémentaire : ceci est fait lors de la migration et la création de la base. - On affiche tout d'abord le contenu de la base (c'est-à-dire rien ou d'anciennes données si la migration est faite à partir de
NounoursDBEntites
) ou le stub (si la migration est faite à partir deStubbedContext
). Notez l'utilisation d'Include
dansdb.NounoursSet.Include(n => n.Carnet)
sinon, lesCarnetDeSante
ne sont pas chargés.Include
n'est pas utilisé ensuite dansdb.Carnets
car lesNounours
ont déjà été chargés depuis la connexion. Mais on aurait pu faire les accès dans l'autre sens et dans ce cas d'aborddb.Carnets.Include(c => c.Owner)
puis simplementdb.NounoursSet
.
using (NounoursDBEntities db = new NounoursDBEntities())
{
WriteLine("Contenu de la base (nounours) : ");
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
{
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
}
WriteLine("Contenu de la base (carnets de santé) : ");
foreach (var c in db.Carnets)
{
WriteLine($"\t{c}");
}
//...
}
- La suite de l'exemple ajoute un nouveau
Nounours
et sonCarnetDeSante
puis affiche le contenu de la base de données.
using (NounoursDBEntities db = new NounoursDBEntities())
{
//...
WriteLine("\nAjout d'un nounours et de son carnet de santé\n");
Nounours porg = new Nounours { Nom = "Porg", DateDeNaissance = new DateTime(2017, 7, 19), NbPoils = 123 };
CarnetDeSante carnetPorg = new CarnetDeSante { LastModified = DateTime.Now, Owner = porg };
porg.Carnet = carnetPorg;
db.AddRange(porg, carnetPorg);
db.SaveChanges();
}
using (NounoursDBEntities db = new NounoursDBEntities())
{
WriteLine("Contenu de la base (nounours) : ");
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
{
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
}
WriteLine("Contenu de la base (carnets de santé) : ");
foreach (var c in db.Carnets)
{
WriteLine($"\t{c}");
}
}
Comment exécuter cet exemple ?
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
- Ouvrez la Console du Gestionnaire de package sous Windows ou le Terminal sous MacOSX, pour cela, dirigez-vous dans le menu Outils, puis Gestionnaire de package NuGet, puis Console du Gestionnaire de package.
- Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
cd .\p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions
Note: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
-
dotnet tool install --global dotnet-ef
si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), -
dotnet tool install --global dotnet-ef --version 3.0.0
si vous vous utiliser spécifiquement .NET Core 3.0.- Migration :
dotnet ef migrations add ex_042_012 --context StubbedContext
- Création de la table :
dotnet ef database update --context StubbedContext
-
Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple ex_042_012_OneToOne_conventions.
-
Le résultat de l'exécution va ressembler à :
Contenu de la base (nounours) :
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
Contenu de la base (carnets de santé) :
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
Ajout d'un nounours et de son carnet de santé
Contenu de la base (nounours) :
1: Chewbacca (27/05/1977, 1234567 poils), LastModified: 20/01/2020
2: Yoda (21/05/1980, 3 poils), LastModified: 21/05/1980
3: Ewok (25/05/1983, 3456789 poils), LastModified: 25/05/1983
4: Porg (19/07/2017, 123 poils), LastModified: 20/01/2020
Contenu de la base (carnets de santé) :
1 : carnet de Chewbacca, modifié la dernière fois le 20/01/2020
2 : carnet de Yoda, modifié la dernière fois le 21/05/1980
3 : carnet de Ewok, modifié la dernière fois le 25/05/1983
4 : carnet de Porg, modifié la dernière fois le 20/01/2020
Note : l'identifiant du dernier Nounours
sera vraisemblablement différent puisqu'il est créé par la base lors de l'insertion.
Comment exécuter cet exemple sans le stub ?
Il suffit de faire exactement comme dans le paragraphe précédent, mais en choisissant le contexte NounoursDBEntities
à la place de StubbedContext
:
dotnet ef migrations add ex_042_012 --context NounoursDBEntities
dotnet ef database update --context NounoursDBEntities
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les 3 nounours du stub.
Comment vérifier quelles base et tables ont été créées et leur contenu ?
Pour vérifier le contenu de votre base SQLite, vous pouvez utiliser le programme DB Browser :
- Rendez-vous sur la page : https://sqlitebrowser.org/dl/ et téléchargez le programme DB Browser.
- Lancez DB Browser for SQLite
- Glissez-déposez au milieu de la fenêtre de DB Browser for SQLite le fichier ex_042_012_OneToOne_conventions.Nounours.db qui a été généré par l'exécution du programme et qui se trouve près de ex_042_012_OneToOne_conventions.csproj.
- Choisissez ensuite l'onglet Parcourir les données
- Observez les résultats obtenus des deux tables