/// Cet exemple montre comment construire une relation 1-1 dans la base de données en utilisant la Fluent API.
/// On préférera cette solution par exemple lorsque nous n'avons pas accès à la classe Model et que nous ne pouvons donc pas utiliser les conventions de nommage
/// comme dans l'exemple précédent.
///
/// on utilise les données stubbées de MyStubDataInitializationStrategy
/// On affiche les Nounours et les Carnets de santé.
/// Constatez que les identifiants sont bien les mêmes à cause de la relation 1-1.
WriteLine("Votre base de données n'existe pas. C'est peut-être la première fois que vous exécutez cet exemple.");
WriteLine("Pour créer la base de données, suivez les instructions suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_007_EF_CF_One_to_One_FluentAPI)");
@ -32,9 +32,10 @@ Ce chapitre s'attardera sur le lien entre le mod
* [**ex_042_008 : Data Seeding before Entity Framework 2.1**](ex_042_008_DataSeeding_before_EF2.1) : explique comment utiliser un stub (méthode qui était recommandée avant EF Core 2.1)
* [**ex_042_009 : Data Seeding**](ex_042_009_DataSeeding) : explique comment utiliser un stub (méthode recommandée depuis EF Core 2.1)
* [**Relationships**](Relationships.md) : en cliquant [ici](Relationships.md), vous aurez plus de détails sur les relations entre entités
* [**ex_042_010 : Single Property navigation with data annotations**](ex_042_010_SinglePropertyNavigation_conventions) : montre comment une relation d'association est traduite par **EF Core** lorsque cette association est unidirectionnelle entre deux entités, en utilisant les conventions d'écriture et/ou les annotations de données.
* [**ex_042_011 : Single Property navigation with Fluent API**](ex_042_011_SinglePropertyNavigation_FluentAPI) : montre comment une relation d'association est traduite par **EF Core** lorsque cette association est unidirectionnelle entre deux entités, en utilisant la *FLuent API*.
* [**ex_042_012 : One To One with data annotations**](ex_042_012_OneToOne_conventions) : montre comment une relation d'association *One To One* est traduite par **EF Core** lorsque cette association est bidirectionnelle entre deux entités, en utilisant l'*annotation de données*.
* [**ex_042_010 : Single Property navigation with data annotations**](ex_042_010_SinglePropertyNavigation_conventions) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant les conventions d'écriture et/ou les annotations de données.
* [**ex_042_011 : Single Property navigation with Fluent API**](ex_042_011_SinglePropertyNavigation_FluentAPI) : montre comment une relation d'association est traduite par *EF Core* lorsque cette association est unidirectionnelle entre deux entités, en utilisant la *FLuent API*.
* [**ex_042_012 : One To One with data annotations**](ex_042_012_OneToOne_conventions) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant l'*annotation de données*.
* [**ex_042_013 : One To One with Fluent API**](ex_042_013_OneToOne_FluentAPI) : montre comment une relation d'association *One To One* est traduite par *EF Core* lorsque cette association est bidirectionnelle entre deux entités, en utilisant la *FluentAPI*.
3. *Schemas and migrations* :
Le but de ce chapitre sera de vous montrer comment garder votre modèle et votre base de données synchronisés.
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**](../ex_042_013_OneToOne_FluentAPI)
Cet exemple montre comment réaliser une relation *One To One* entre deux entités avec *Entity Framework Core* et la *Fluent API*.
Une version équivalente réalisée avec les *annotations de données* est disponible dans l'exemple
[**ex_042_012 : One To One with data annotations**](../ex_042_012_OneToOne_conventions)
---
@ -23,7 +23,7 @@ Une version équivalente réalisée avec la *Fluent API* est disponible dans l'e
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```.
* ```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")```.
```csharp
[Key, ForeignKey("Owner")]
public int UniqueId
{
get; set;
}
```
* En conséquence, l'identifiant unique ```UniqueId``` de ```CarnetDeSante``` aura la même valeur que l'```UniqueId``` d'une entité ```Nounours```.
* ```Nounours``` possède un identifiant unique ```UniqueId```.
* ```CarnetDeSante``` possède un identifiant unique ```UniqueId```.
### La classe ```NounoursDBEntities```
@ -56,9 +46,80 @@ public int UniqueId
public DbSet<Nounours> NounoursSet { get; set; }
public DbSet<CarnetDeSante> Carnets { get; set; }
```
##### Quelques explications supplémentaires :
* La classe réécrit ensuite la méthode ```OnModelCreating``` :
.HasMaxLength(256); //définition de la colonne Nom
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date"); //changement du nom de la colonne Naissance
//création de la table "Carnets"
modelBuilder.Entity<CarnetDeSante>().ToTable("Carnets"); // nom de la table
modelBuilder.Entity<CarnetDeSante>().HasKey(c => c.UniqueId); //définition de la clé primaire
On s'intéresse enfin à la **relation _One To One_**, où l'on précise qu'une entité ```Nounours``` possède une association vers l'entité de type ```CarnetDeSante``` grâce à la propriété ```Carnet``` (```.HasOne(n => n.Carnet)```).
Cette entité ```CarnetDeSante``` possède elle-même une association vers une entité de type ```Nounours``` grâce à sa propriété ```Owner``` (```.WithOne(c => c.Owner)```),
et on précise que cette entité ```CarnetDeSante``` voit sa propriété ```UniqueId``` utilisée comme clé étrangère (```.HasForeignKey<CarnetDeSante>(c => c.UniqueId)```). Elle automatiquement reliée à la clé primaire de ```Nounours```.
.HasOne(n => n.Carnet) //a une propriété obligatoire Carnet...
.WithOne(c => c.Owner) //reliée à la propriété Owner du Carnet...
.HasForeignKey<CarnetDeSante>(c => c.UniqueId);//dont la propriété UniqueId est une Foreign Key
```
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.
C'est le cas grâce au code précédent qui s'occupe de définir de quelle manière les deux entités sont reliées.
### La classe ```StubbedContext```
@ -147,7 +208,7 @@ Pour tester cette application, n'oubliez pas les commandes comme présentées da
* 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
cd .\p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI
```
*Note*:
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
@ -159,14 +220,14 @@ cd .\p08_BDD_EntityFramework\ex_042_012_OneToOne_conventions
* Migration :
```
dotnet ef migrations add ex_042_012 --context StubbedContext
dotnet ef migrations add ex_042_013 --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**.
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_013_OneToOne_FluentAPI**.
* Le résultat de l'exécution va ressembler à :
```
@ -197,7 +258,7 @@ Contenu de la base (carnets de santé) :
## 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 migrations add ex_042_013 --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.
@ -206,11 +267,11 @@ Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aur
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*.
![DB Browser for SQLite](./readme_files/dbbrowser01.png)
* Glissez-déposez au milieu de la fenêtre de *DB Browser for SQLite* le fichier *ex_042_013_OneToOne_FluentAPI.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*.
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser01.png)
* Choisissez ensuite l'onglet *Parcourir les données*
* Observez les résultats obtenus des deux tables
![DB Browser for SQLite](./readme_files/dbbrowser02.png)
![DB Browser for SQLite](./readme_files/dbbrowser03.png)
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser02.png)
![DB Browser for SQLite](../ex_042_012_OneToOne_conventions/readme_files/dbbrowser03.png)