begun ex_042_015

EFCore3_Reforged
Marc CHEVALDONNE 5 years ago
parent ad341cb1bd
commit 3402c70c71

@ -580,7 +580,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_012_OneToOne_convent
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_013_OneToOne_FluentAPI", "p08_BDD_EntityFramework\ex_042_013_OneToOne_FluentAPI\ex_042_013_OneToOne_FluentAPI.csproj", "{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_014_OneToMany_dataAnnotations", "p08_BDD_EntityFramework\ex_042_014_OneToMany_dataAnnotations\ex_042_014_OneToMany_dataAnnotations.csproj", "{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_014_OneToMany_dataAnnotations", "p08_BDD_EntityFramework\ex_042_014_OneToMany_dataAnnotations\ex_042_014_OneToMany_dataAnnotations.csproj", "{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_015_OneToMany_conventions", "p08_BDD_EntityFramework\ex_042_015_OneToMany_conventions\ex_042_015_OneToMany_conventions.csproj", "{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -4798,6 +4800,26 @@ Global
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x64.Build.0 = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x86.ActiveCfg = Release|Any CPU
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C}.Release|x86.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|ARM64.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x64.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x64.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x86.ActiveCfg = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Debug|x86.Build.0 = Debug|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|Any CPU.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM64.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|ARM64.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x64.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x64.Build.0 = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x86.ActiveCfg = Release|Any CPU
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -5071,6 +5093,7 @@ Global
{762C349D-5685-43FA-A077-2F3BDD07C898} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{ADF1001A-AF43-4D6E-9D4A-B0C26E4E19D5} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{BE5CC150-CBCA-4E5F-BC7B-4F4A8E93380C} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
{C85BE497-5BA9-4D7E-93EF-8F44E06DFDBC} = {C7672736-AA73-4B7E-B5C6-A7A984216372}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E}

@ -1,5 +1,5 @@
# Entity Framework Core 3.0
*20/01/2020 ⋅ Marc Chevaldonné*
*21/01/2020 ⋅ Marc Chevaldonné*
---
Entity Framework (EF) Core est un ORM (Object-Relational Mapper) qui permet aux développeurs .NET de gérer de manière simple, légère et extensible, des bases de données.
@ -36,6 +36,7 @@ Ce chapitre s'attardera sur le lien entre le mod
* [**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*.
* [**ex_042_014 : One To Many with data annotations**](ex_042_014_OneToMany_dataAnnotations) : montre comment une relation d'association *One To Many* est traduite par *EF Core* en utilisant l'*annotation de données*.
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.
4. *Querying (LINQ to SQL) and saving data* :

@ -5,8 +5,8 @@
---
Cet exemple montre comment réaliser une relation *One To Many* entre deux entités avec *Entity Framework Core* et l'*annotation de données*.
Une version équivalent réalisée avec les *conventions d'écriture* et l'établissement d'une clé étrangère de manière implicite est disponible dans l'exemple
[**ex_042_015 : One To Many with convenions**](../ex_042_015_OneToMany_conventions)
Une version équivalente réalisée avec les *conventions d'écriture* et l'établissement d'une clé étrangère de manière implicite est disponible dans l'exemple
[**ex_042_015 : One To Many with conventions**](../ex_042_015_OneToMany_conventions)
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
[**ex_042_016 : One To Many with Fluent API**](../ex_042_016_OneToMany_FluentAPI)
@ -43,7 +43,7 @@ public Album Album { get; set; }
* Cet identifiant est généré par la base : ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]```.
* Le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau``` est lié.
Dans le cas de l'utilisation des *conventions d'écriture* et des *annotations de données*, ceci peut se faire explicitement ou implicitement. Cet exemple, le fait de manière explicite avec les *annotations de données*. Mais il est possible
de le faire de manière explicite en utilisant les *conventions d'écriture* (cf. [ex_042_015 : One To Many with convenions](../ex_042_015_OneToMany_conventions))
de le faire de manière implicite en utilisant les *conventions d'écriture* (cf. [ex_042_015 : One To Many with convenions](../ex_042_015_OneToMany_conventions))
ou de manière explicite en utilisant la *Fluent API* (cf. [ex_042_016 : One To Many with Fluent API](../ex_042_016_OneToMany_FluentAPI)). Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
Ici, on ajoute donc une nouvelle propriété à ```Morceau``` qui va permettre de stocker l'identifiant unique de ```Album``` (cette propriété doit donc être du même type que la clé primaire de ```Albuml```) :
```csharp
@ -74,8 +74,8 @@ public DbSet<Morceau> Morceaux { get; set; }
##### Quelques explications supplémentaires :
Comme dit plus haut, le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau``` est lié.
Dans le cas de l'utilisation des *conventions d'écriture* et des *annotations de données*, ceci peut se faire explicitement ou implicitement. Cet exemple, le fait de manière explicite avec les *annotations de données*. Mais il est possible
de le faire de manière explicite en utilisant les *conventions d'écriture* (cf. [ex_042_015 : One To Many with convenions](../ex_042_015_OneToMany_conventions))
ou de manière explicite en utilisant la *Fluent API* (cf. [ex_042_016 : One To Many with Fluent API](../ex_042_016_OneToMany_FluentAPI)). Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
de le faire de manière implicite en utilisant les *conventions d'écriture* (cf. [ex_042_015 : One To Many with convenions](../ex_042_015_OneToMany_conventions))
ou de manière explicite en utilisant la *Fluent API* (cf. [ex_042_016 : One To Many with Fluent API](../ex_042_016_OneToMany_FluentAPI)).
### La classe ```StubbedContext```

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_015_OneToMany_conventions
{
/// <summary>
/// Album est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation 1-many avec la classe Morceau via la propriété Morceaux.
/// La clé primaire est générée lors de l'insertion en table.
/// </summary>
public class Album
{
public int AlbumId
{
get; set;
}
public string Titre
{
get; set;
}
public DateTime DateDeSortie
{
get; set;
}
public ICollection<Morceau> Morceaux { get; set; } = new List<Morceau>();
}
}

@ -0,0 +1,19 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_015_OneToMany_conventions
{
/// <summary>
/// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
/// Cette classe contient deux DbSet<T> pour permettre de réaliser des opérations CRUD sur les types T, ici Album et Morceau.
/// </summary>
public class AlbumDBEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Morceau> Morceaux { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Data Source=ex_042_015_OneToMany_conventions.Albums.db");
}
}
}

@ -0,0 +1,29 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ex_042_015_OneToMany_conventions
{
/// <summary>
/// Morceau est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation 1-many avec la classe Morceau via la propriété Album.
/// La clé primaire est générée lors de l'insertion en table.
/// </summary>
public class Morceau
{
public int MorceauId
{
get; set;
}
public string Titre
{
get; set;
}
public Album Album
{
get; set;
}
}
}

@ -0,0 +1,98 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System;
using static System.Console;
namespace ex_042_015_OneToMany_conventions
{
class Program
{
/// <summary>
/// Cet exemple montre comment construire une relation 1-many dans la base de données en utilisant les conventions de nommage Entity Framework.
///
/// On affiche les Albums et les Morceaux.
/// Constatez que les identifiants sont bien les mêmes à cause de la relation 1-many.
///
/// Si vous ouvrez la base de données (via l'explorateur d'objets SQL Server), vous pourrez constater que la table Morceaux
/// contient une colonne Album_UniqueId qui permet d'assurer la relation 1-many.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
try
{
using (AlbumDBEntities db = new AlbumDBEntities())
{
WriteLine("Albums : ");
foreach (var a in db.Albums.Include(a => a.Morceaux))
{
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
foreach (var m in a.Morceaux)
{
WriteLine($"\t\t{m.Titre}");
}
}
WriteLine();
WriteLine("Morceaux :");
foreach (var m in db.Morceaux)
{
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
}
WriteLine("\nAjout d'un album et 6 morceaux...\n");
Album captainMarvel = new Album { Titre = "Captain Marvel", DateDeSortie = new DateTime(1972, 3, 3) };
Morceau[] morceaux = { new Morceau { Titre = "La Fiesta", Album = captainMarvel },
new Morceau { Titre = "Five Hundred Miles High", Album = captainMarvel },
new Morceau { Titre = "Captain Marvel", Album = captainMarvel },
new Morceau { Titre = "Time's Lie", Album = captainMarvel },
new Morceau { Titre = "Lush Life", Album = captainMarvel },
new Morceau { Titre = "Day Waves", Album = captainMarvel }
};
foreach (var m in morceaux)
{
captainMarvel.Morceaux.Add(m);
}
db.Add(captainMarvel);
db.SaveChanges();
}
using (AlbumDBEntities db = new AlbumDBEntities())
{
WriteLine("Albums : ");
foreach (var a in db.Albums.Include(a => a.Morceaux))
{
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
foreach (var m in a.Morceaux)
{
WriteLine($"\t\t{m.Titre}");
}
}
WriteLine();
WriteLine("Morceaux :");
foreach (var m in db.Morceaux)
{
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
}
}
}
catch (NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (SqliteException exc)
{
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 données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,348 @@
# ex_042_015_OneToMany_conventions
*22/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple montre comment réaliser une relation *One To Many* entre deux entités
avec *Entity Framework Core* et les *conventions d'écriture* avec génération
implicite d'une clé étrangère.
Une version équivalente réalisée avec les *annotations de données* et
l'établissement d'une clé étrangère de manière explicite est disponible dans
l'exemple
[**ex_042_014 : One To Many with data annotations**](../ex_042_015_OneToMany_dataAnnotations)
Une version équivalente réalisée avec la *Fluent API* est disponible dans l'exemple
[**ex_042_016 : One To Many with Fluent API**](../ex_042_016_OneToMany_FluentAPI)
---
## Comment est construit cet exemple ?
* Le projet est de type .NET Core
* Il contient quatre classes :
* ```Album```
* ```Morceau```
* ```AlbumDBEntities```
* ```StubbedContext```
### Les classes entités : ```Album``` et ```Morceau```
La relation **One To Many** est mise en évidence de la manière suivante :
un ```Album``` possède plusieurs ```Morceau```, et un ```Morceau``` possède
un ```Album```.
Un ```Album``` contient différentes propriétés (```Titre```, ```DateDeSortie```)
dont ```Morceaux``` de type ```ICollection<Morceau>```.
Un ```Morceau``` possède une propriété de type ```string``` (```Titre```), et
une propriété ```Album``` de type ```Album```.
<img src="../ex_042_014_OneToMany_dataAnnotations/readme_files/ex_042_014_classDiagram.svg"/>
Ce qu'il faut noter :
* ```Album``` possède une association vers ```Morceau```
```csharp
public ICollection<Morceau> Morceaux { get; set; } = new List<Morceau>();
```
* ```Morceau``` possède une association vers ```Album```
```csharp
public Album Album { get; set; }
```
* ```Album``` possède un identifiant unique ```AlbumId``` (qui par convention
d'écriture, sera la clé primaire et sera généré par la base lors de l'insertion).
* ```Morceau``` possède un identifiant unique ```MorceauId``` (qui par convention
d'écriture, sera la clé primaire et sera généré par la base lors de l'insertion).
* Le lien entre les deux tables va se faire avec l'ajout d'une colonne dans la table
de ```Morceau``` qui permettra de stocker l'```Album``` auquel ce ```Morceau```
est lié.
Dans le cas de l'utilisation des *conventions d'écriture*, ceci peut se faire
explicitement ou implicitement. Cet exemple, le fait de manière implicite avec
les *conventions d'écriture*. Mais il est possible de le faire de manière explicite
en utilisant les *annotations de données* (cf. [ex_042_014 : One To Many with data annotations](../ex_042_014_OneToMany_dataAnnotations))
ou de manière explicite en utilisant la *Fluent API* (cf. [ex_042_016 : One To Many with Fluent API](../ex_042_016_OneToMany_FluentAPI)).
Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter
cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
Ici, on __N'__ajoute __PAS__ de nouvelle propriété à ```Morceau``` pour stocker
l'identifiant unique de ```Album``` comme dans l'exemple [ex_042_014 : One To Many with data annotations](../ex_042_014_OneToMany_dataAnnotations).
### La classe ```AlbumDBEntities```
* Comme dans les exemples précédents, ```AlbumDBEntities``` dérive de ```DbContext```.
* ```AlbumDBEntities``` déclare deux ```DbSet``` : un de ```Album``` et
l'autre de ```Morceau```.
```csharp
public DbSet<Album> Albums { get; set; }
public DbSet<Morceau> Morceaux { get; set; }
```
##### Quelques explications supplémentaires :
Comme dit plus haut, le lien entre les deux tables va se faire avec l'ajout d'une
colonne dans la table de ```Morceau``` qui permettra de stocker l'```Album```
auquel ce ```Morceau``` est lié.
Dans le cas de l'utilisation des *conventions d'écriture* et des
*annotations de données*, ceci peut se faire explicitement ou implicitement.
Cet exemple, le fait de manière implicite avec les *conventions d'écriture*. Mais il
est possible de le faire de manière explicite en utilisant les *annotations de données*
(cf. [ex_042_014 : One To Many with dataAnnotations](../ex_042_014_OneToMany_dataAnnotations))
ou de manière explicite en utilisant la *Fluent API* (cf. [ex_042_016 : One To Many with Fluent API](../ex_042_016_OneToMany_FluentAPI)).
Ici, c'est *EF Core* qui ajoute automatiquement une propriété ```AlbumId```
à ```Morceau``` : c'est la clé étrangère. Le nom de cette propriété est choisi à
partir du nom de la propriété allant de ```Morceau``` à ```Album``` (ici ```Album```) +
le type pointé par la clé étrangère (ici ```Album```) + ```Id``` => +
ici ```AlbumAlbumId```.
Tout ceci est fait automatiquement.
<img src="./readme_files/ex_042_015_classDiagram2.svg"/>
Toutefois, pour l'utilisation de données *stubbées*, il est nécessaire d'expliciter
cette propriété (cf. plus bas, lors de l'utilisation de ```StubbedContext```).
### La classe ```StubbedContext```
* ```StubbedContext``` est une classe fille de ```AlbumDBEntities```.
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éthode ```OnModelCreating``` qui appelle la méthode de la classe mère puis
ajoute des instances d'entités, grâce à la méthode d'extension ```HasData```.
```csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Album kindofblue = new Album { AlbumId=1, Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
Album dialogue = new Album { AlbumId=2, Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
modelBuilder.Entity<Album>().HasData(kindofblue, dialogue);
modelBuilder.Entity<Morceau>().HasData(new Morceau { MorceauId = 1, AlbumForeignKey = 1, Titre = "So What" },
new Morceau { MorceauId = 2, AlbumForeignKey = 1, Titre = "Freddie Freeloader" },
new Morceau { MorceauId = 3, AlbumForeignKey = 1, Titre = "Blue in Green" },
new Morceau { MorceauId = 4, AlbumForeignKey = 1, Titre = "All Blues" },
new Morceau { MorceauId = 5, AlbumForeignKey = 1, Titre = "Flamenco Sketches" },
new Morceau { MorceauId = 6, AlbumForeignKey = 2, Titre = "Catta" },
new Morceau { MorceauId = 7, AlbumForeignKey = 2, Titre = "Idle While" },
new Morceau { MorceauId = 8, AlbumForeignKey = 2, Titre = "Les Noirs Marchant" },
new Morceau { MorceauId = 9, AlbumForeignKey = 2, Titre = "Dialogue" },
new Morceau { MorceauId = 10, AlbumForeignKey = 2, Titre = "Ghetto Lights" },
new Morceau { MorceauId = 11, AlbumForeignKey = 2, Titre = "Jasper" } );
}
```
* Remarquez que __À AUCUN MOMENT__ nous ne précisons les valeurs des propriétés ```Album``` de ```Morceau``` et ```Morceaux``` de ```Album```.
Le simple fait d'utiliser la clé étrangère (propriété ```AlbumForeignKey```) dans ```Morceau``` est suffisant.
* 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 ```Morceaux``` et ```Album```.
### La classe ```Program```
* 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 ```AlbumDBEntites```) ou le stub (si la migration est faite à partir de ```StubbedContext```).
*Notez l'utilisation d'```Include``` dans ```db.Albums.Include(a => a.Morceaux)``` sinon, les ```Morceau``` ne sont pas chargés.
```Include``` n'est pas utilisé ensuite dans ```db.Morceaux``` car les ```Album``` 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'abord ```db.Morceaux.Include(m => m.Album)``` puis simplement ```db.Albums```.*
```csharp
using (AlbumDBEntities db = new AlbumDBEntities())
{
WriteLine("Albums : ");
foreach (var a in db.Albums.Include(a => a.Morceaux))
{
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
foreach (var m in a.Morceaux)
{
WriteLine($"\t\t{m.Titre}");
}
}
WriteLine();
WriteLine("Morceaux :");
foreach (var m in db.Morceaux)
{
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
}
//...
}
```
* La suite de l'exemple ajoute un nouvel ```Album``` et ses ```Morceau``` puis affiche le contenu de la base de données.
```csharp
using (AlbumDBEntities db = new AlbumDBEntities())
{
//...
WriteLine("\nAjout d'un album et 6 morceaux...\n");
Album captainMarvel = new Album { Titre = "Captain Marvel", DateDeSortie = new DateTime(1972, 3, 3) };
Morceau[] morceaux = { new Morceau { Titre = "La Fiesta", Album = captainMarvel },
new Morceau { Titre = "Five Hundred Miles High", Album = captainMarvel },
new Morceau { Titre = "Captain Marvel", Album = captainMarvel },
new Morceau { Titre = "Time's Lie", Album = captainMarvel },
new Morceau { Titre = "Lush Life", Album = captainMarvel },
new Morceau { Titre = "Day Waves", Album = captainMarvel }
};
foreach (var m in morceaux)
{
captainMarvel.Morceaux.Add(m);
}
db.Add(captainMarvel);
db.SaveChanges();
}
```
```csharp
using (AlbumDBEntities db = new AlbumDBEntities())
{
WriteLine("Albums : ");
foreach (var a in db.Albums.Include(a => a.Morceaux))
{
WriteLine($"\t{a.AlbumId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
foreach (var m in a.Morceaux)
{
WriteLine($"\t\t{m.Titre}");
}
}
WriteLine();
WriteLine("Morceaux :");
foreach (var m in db.Morceaux)
{
WriteLine($"\t{m.MorceauId}: {m.Titre} (album : {m.Album.Titre})");
}
}
```
## 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 (pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*) ou le *Terminal* sous MacOSX.
* 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_014_OneToMany_dataAnnotations
```
*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 : vous devez préciser la classe fille de ```DbContext``` à utiliser : soit ```AlbumDBEntities```, soit ```StubbedContext```.
```
dotnet ef migrations add ex_042_014 --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_014_OneToMany_dataAnnotations**.
* Le résultat de l'exécution va ressembler à (si vous avez utilisé ```StubbedContext```) :
```
Albums :
1: Kind of Blue (sorti le : 17/08/1959)
So What
Freddie Freeloader
Blue in Green
All Blues
Flamenco Sketches
2: Dialogue (sorti le : 01/09/1965)
Catta
Idle While
Les Noirs Marchant
Dialogue
Ghetto Lights
Jasper
Morceaux :
1: So What (album : Kind of Blue)
2: Freddie Freeloader (album : Kind of Blue)
3: Blue in Green (album : Kind of Blue)
4: All Blues (album : Kind of Blue)
5: Flamenco Sketches (album : Kind of Blue)
6: Catta (album : Dialogue)
7: Idle While (album : Dialogue)
8: Les Noirs Marchant (album : Dialogue)
9: Dialogue (album : Dialogue)
10: Ghetto Lights (album : Dialogue)
11: Jasper (album : Dialogue)
Ajout d'un album et 6 morceaux...
Albums :
1: Kind of Blue (sorti le : 17/08/1959)
So What
Freddie Freeloader
Blue in Green
All Blues
Flamenco Sketches
2: Dialogue (sorti le : 01/09/1965)
Catta
Idle While
Les Noirs Marchant
Dialogue
Ghetto Lights
Jasper
3: Captain Marvel (sorti le : 03/03/1972)
La Fiesta
Five Hundred Miles High
Captain Marvel
Time's Lie
Lush Life
Day Waves
Morceaux :
1: So What (album : Kind of Blue)
2: Freddie Freeloader (album : Kind of Blue)
3: Blue in Green (album : Kind of Blue)
4: All Blues (album : Kind of Blue)
5: Flamenco Sketches (album : Kind of Blue)
6: Catta (album : Dialogue)
7: Idle While (album : Dialogue)
8: Les Noirs Marchant (album : Dialogue)
9: Dialogue (album : Dialogue)
10: Ghetto Lights (album : Dialogue)
11: Jasper (album : Dialogue)
12: La Fiesta (album : Captain Marvel)
13: Five Hundred Miles High (album : Captain Marvel)
14: Captain Marvel (album : Captain Marvel)
15: Time's Lie (album : Captain Marvel)
16: Lush Life (album : Captain Marvel)
17: Day Waves (album : Captain Marvel)
```
## 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 ```AlbumDBEntities``` à la place de ```StubbedContext``` :
```
dotnet ef migrations add ex_042_014 --context AlbumDBEntities
dotnet ef database update --context AlbumDBEntities
```
Lors de l'exécution, le résultat sera évidemment différent puisqu'il n'y aura pas les 2 albums du stub.
Il pourra ressembler à :
```
Albums :
Morceaux :
Ajout d'un album et 6 morceaux...
Albums :
1: Captain Marvel (sorti le : 03/03/1972)
La Fiesta
Five Hundred Miles High
Captain Marvel
Time's Lie
Lush Life
Day Waves
Morceaux :
1: La Fiesta (album : Captain Marvel)
2: Five Hundred Miles High (album : Captain Marvel)
3: Captain Marvel (album : Captain Marvel)
4: Time's Lie (album : Captain Marvel)
5: Lush Life (album : Captain Marvel)
6: Day Waves (album : Captain Marvel)
```
## 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_014_OneToMany_dataAnnotations.Nounours.db* qui a été généré par l'exécution du programme et qui se trouve près de *ex_042_014_OneToMany_dataAnnotations.csproj*.
![DB Browser for SQLite](./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)

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Text;
namespace ex_042_015_OneToMany_conventions
{
class StubbedContext : AlbumDBEntities
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
Album kindofblue = new Album { AlbumId=1, Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) };
Album dialogue = new Album { AlbumId=2, Titre = "Dialogue", DateDeSortie = new DateTime(1965, 9, 1) };
modelBuilder.Entity<Album>().HasData(kindofblue, dialogue);
modelBuilder.Entity<Morceau>().Property<int>("AlbumId");
modelBuilder.Entity<Morceau>().HasData(new { MorceauId = 1, AlbumId = 1, Titre = "So What" },
new { MorceauId = 2, AlbumId = 1, Titre = "Freddie Freeloader" },
new { MorceauId = 3, AlbumId = 1, Titre = "Blue in Green" },
new { MorceauId = 4, AlbumId = 1, Titre = "All Blues" },
new { MorceauId = 5, AlbumId = 1, Titre = "Flamenco Sketches" },
new { MorceauId = 6, AlbumId = 2, Titre = "Catta" },
new { MorceauId = 7, AlbumId = 2, Titre = "Idle While" },
new { MorceauId = 8, AlbumId = 2, Titre = "Les Noirs Marchant" },
new { MorceauId = 9, AlbumId = 2, Titre = "Dialogue" },
new { MorceauId = 10, AlbumId = 2, Titre = "Ghetto Lights" },
new { MorceauId = 11, AlbumId = 2, Titre = "Jasper" }
);
}
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<StartWorkingDirectory>$(MSBuildProjectDirectory)</StartWorkingDirectory>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SQLite" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -0,0 +1,129 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="520.87255859375" height="174">
<defs/>
<g>
<g transform="translate(-94,-230) scale(1,1)">
<rect fill="#C0C0C0" stroke="none" x="111" y="247" width="157.71923828125" height="87" opacity="0.2"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<rect fill="#ffffff" stroke="none" x="104" y="240" width="157.71923828125" height="87"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 104 240 L 261.71923828125 240 L 261.71923828125 327 L 104 327 L 104 240 Z Z" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 104 265 L 261.71923828125 265" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 104 318 L 261.71923828125 318" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="bold" text-decoration="none" x="164.435546875" y="259.5">Album</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="109" y="282.5">+AlbumId: int</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="109" y="297.5">+Titre: string</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="109" y="312.5">+DateDeSortie: DateTime</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<rect fill="#C0C0C0" stroke="none" x="471" y="247" width="140.87255859375" height="87" opacity="0.2"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<rect fill="#ffffff" stroke="none" x="464" y="240" width="140.87255859375" height="87"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 464 240 L 604.87255859375 240 L 604.87255859375 327 L 464 327 L 464 240 Z Z" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 464 265 L 604.87255859375 265" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 464 318 L 604.87255859375 318" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="bold" text-decoration="none" x="509.147216796875" y="259.5">Morceau</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="469" y="282.5">+MorceauId: int</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="469" y="297.5">+Titre: string</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#c00000" stroke="none" font-family="Arial" font-size="13px" font-style="italic" font-weight="normal" text-decoration="none" x="469" y="312.5">+AlbumAlbumId: int</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 263 283 L 463 283" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="265.5" y="274.5">+Album</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="280.5" y="301.5">1</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="389.5" y="275.5">+Morceaux</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="438.5" y="301.5">*</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="#ffff91" stroke="none" d="M 336 344 L 453.97900390625 344 L 463.97900390625 354 L 463.97900390625 393 L 336 393 L 336 344"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 336 344 L 453.97900390625 344 L 463.97900390625 354 L 463.97900390625 393 L 336 393 L 336 344 L 336 344" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 453.97900390625 344 L 453.97900390625 354 L 463.97900390625 354 L 453.97900390625 344" stroke-miterlimit="10"/>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<g>
<path fill="none" stroke="none"/>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="341" y="362">ForeignKey :</text>
<text fill="#000000" stroke="none" font-family="Arial" font-size="13px" font-style="normal" font-weight="normal" text-decoration="none" x="341" y="375">AlbumAlbumId</text>
</g>
</g>
<g transform="translate(-94,-230) scale(1,1)">
<path fill="none" stroke="#000000" d="M 464 328 L 441 343" stroke-miterlimit="10" stroke-dasharray="3"/>
</g>
</g>
</svg>

@ -20,7 +20,7 @@
* V 010, 011, single navigation property
* cascade delete
* V 012 013 one to one
* one to many
* 014 one to many
* many to many
* dictionaries
* 012 013 foreign key

Loading…
Cancel
Save