From fe46bd2aa2875beceff9d4a00e88b72f694205a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Chevaldonn=C3=A9?= Date: Sat, 25 Jan 2020 15:21:21 +0100 Subject: [PATCH] added sample ex_042_016; readme to be finished --- Exemples.sln | 25 +- .../ex_042_016_OneToMany_FluentAPI/Album.cs | 31 ++ .../AlbumDBEntities.cs | 44 ++ .../ex_042_016_OneToMany_FluentAPI/Morceau.cs | 28 ++ .../ex_042_016_OneToMany_FluentAPI/Program.cs | 98 +++++ .../ex_042_016_OneToMany_FluentAPI/ReadMe.md | 407 ++++++++++++++++++ .../StubbedContext.cs | 33 ++ .../ex_042_016_OneToMany_FluentAPI.csproj | 15 + 8 files changed, 680 insertions(+), 1 deletion(-) create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Album.cs create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/AlbumDBEntities.cs create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Morceau.cs create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Program.cs create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ReadMe.md create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/StubbedContext.cs create mode 100644 p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ex_042_016_OneToMany_FluentAPI.csproj diff --git a/Exemples.sln b/Exemples.sln index 53d7d56..ff3ad25 100644 --- a/Exemples.sln +++ b/Exemples.sln @@ -580,7 +580,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_013_OneToOne_FluentA EndProject 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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_016_OneToMany_FluentAPI", "p08_BDD_EntityFramework\ex_042_016_OneToMany_FluentAPI\ex_042_016_OneToMany_FluentAPI.csproj", "{9C2D4443-635F-4BA7-9870-9705BAD534EF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -4798,6 +4800,26 @@ Global {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 + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM.Build.0 = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|ARM64.Build.0 = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x64.ActiveCfg = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x64.Build.0 = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Debug|x86.Build.0 = Debug|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|Any CPU.Build.0 = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM.ActiveCfg = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM.Build.0 = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM64.ActiveCfg = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|ARM64.Build.0 = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x64.ActiveCfg = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x64.Build.0 = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x86.ActiveCfg = Release|Any CPU + {9C2D4443-635F-4BA7-9870-9705BAD534EF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -5071,6 +5093,7 @@ Global {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} + {9C2D4443-635F-4BA7-9870-9705BAD534EF} = {C7672736-AA73-4B7E-B5C6-A7A984216372} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Album.cs b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Album.cs new file mode 100644 index 0000000..f317fa1 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Album.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_016_OneToMany_FluentAPI +{ + /// + /// 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. + /// + public class Album + { + public int AlbumId + { + get; set; + } + + public string Titre + { + get; set; + } + + public DateTime DateDeSortie + { + get; set; + } + + public ICollection Morceaux { get; set; } = new List(); + } +} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/AlbumDBEntities.cs b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/AlbumDBEntities.cs new file mode 100644 index 0000000..e99fbc7 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/AlbumDBEntities.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; + +namespace ex_042_016_OneToMany_FluentAPI +{ + /// + /// 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 pour permettre de réaliser des opérations CRUD sur les types T, ici Album et Morceau. + /// + public class AlbumDBEntities : DbContext + { + public DbSet Albums { get; set; } + public DbSet Morceaux { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlite($"Data Source=ex_042_016_OneToMany_FluentAPI.Albums.db"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + //création de la table Album + modelBuilder.Entity().HasKey(a => a.AlbumId); //définition de la clé primaire + modelBuilder.Entity().Property(a => a.AlbumId) + .ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion + + //création de la table Morceau + modelBuilder.Entity().HasKey(m => m.MorceauId); //définition de la clé primaire + modelBuilder.Entity().Property(m => m.MorceauId) + .ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion + + // Add the shadow property to the model + modelBuilder.Entity() + .Property("AlbumForeignKey"); + + // Use the shadow property as a foreign key + modelBuilder.Entity() + .HasOne(m => m.Album) + .WithMany(a => a.Morceaux) + .HasForeignKey("AlbumForeignKey"); + + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Morceau.cs b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Morceau.cs new file mode 100644 index 0000000..5c2af00 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Morceau.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_016_OneToMany_FluentAPI +{ + /// + /// 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. + /// + public class Morceau + { + public int MorceauId + { + get; set; + } + + public string Titre + { + get; set; + } + + public Album Album + { + get; set; + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Program.cs b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Program.cs new file mode 100644 index 0000000..7f9771b --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/Program.cs @@ -0,0 +1,98 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using System; +using static System.Console; + +namespace ex_042_016_OneToMany_FluentAPI +{ + class Program + { + /// + /// 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. + /// + /// + 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(); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ReadMe.md b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ReadMe.md new file mode 100644 index 0000000..878b85a --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ReadMe.md @@ -0,0 +1,407 @@ +# ex_042_016_OneToMany_FluentAPI + +*22/01/2020 ⋅ Marc Chevaldonné* + +--- + +Cet exemple montre comment réaliser une relation *One To Many* entre deux entités +avec *Entity Framework Core* et la *Fluent API*. +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 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) + +--- + +## 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```. +Un ```Morceau``` possède une propriété de type ```string``` (```Titre```), et +une propriété ```Album``` de type ```Album```. + + +Ce qu'il faut noter : +* ```Album``` possède une association vers ```Morceau``` +```csharp +public ICollection Morceaux { get; set; } = new List(); +``` +* ```Morceau``` possède une association vers ```Album``` +```csharp +public Album Album { get; set; } +``` +* ```Album``` possède un identifiant unique ```AlbumId``` (qui sera la clé +* primaire et sera généré par la base lors de l'insertion, cf. ```AlbumDBEntities```). +* ```Morceau``` possède un identifiant unique ```MorceauId``` (qui sera la clé +* primaire et sera généré par la base lors de l'insertion, cf. ```AlbumDBEntities```). +* 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 de la *Fluent API*, ceci peut se faire dans la classe +qui dérive de ```DbContext```. 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 implicite en utilisant les *conventions d'écriture* +(cf. [ex_042_015 : One To Many with conventions](../ex_042_016_OneToMany_conventions)). +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), +mais on pourrait le faire (cf. commentaires plus bas). + +### 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 Albums { get; set; } +public DbSet Morceaux { get; set; } +``` +Dans la méthode ```OnModelCreating``` : +* on définit la clé primaire de ```Album``` (ceci n'est pas obligatoire, puisqu'on +ne fait que refaire ce que font déjà les convetions d'écriture). +```csharp +//création de la table Album +modelBuilder.Entity().HasKey(a => a.AlbumId); //définition de la clé primaire +modelBuilder.Entity().Property(a => a.AlbumId) + .ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion +``` +* on définit la clé primaire de ```Morceau``` (ceci n'est pas obligatoire, puisqu'on +ne fait que refaire ce que font déjà les convetions d'écriture). +```csharp +//création de la table Morceau +modelBuilder.Entity().HasKey(m => m.MorceauId); //définition de la clé primaire +modelBuilder.Entity().Property(m => m.MorceauId) + .ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion +``` +* on ajoute la clé étrangère dans l'entité ```Morceau``` qui permettra d'établir la +relation OnToMany avec l'```Album``` concerné +```csharp +// Add the shadow property to the model +modelBuilder.Entity() + .Property("AlbumForeignKey"); +``` + +TO BE FINISHED + + +##### 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 d'une combinaison du nom de la propriété allant de ```Morceau``` à ```Album``` (ici ```Album```), +du type pointé par la clé étrangère (ici ```Album```), et de ```Id``` => ici *EF Core* +a *choisi* ```AlbumId```. +Tout ceci est fait automatiquement. + +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().HasData(kindofblue, dialogue); + + modelBuilder.Entity().Property("AlbumId"); + + modelBuilder.Entity().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" } + ); +} +``` +* __Attention toutefois__, ici, puisque nous devons donner la clé étrangère, il faut +d'abord ajouter une colonne (une propriété) à notre entité ```Morceau```, qui permettra +de stocker la clé étrangère pour pointer l'```Album``` associé : +```csharp +modelBuilder.Entity().Property("AlbumId"); +``` +* 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é ```AlbumId``` +ajoutée précédemment) 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_015_OneToMany_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 : vous devez préciser la classe fille de ```DbContext``` à utiliser : +soit ```AlbumDBEntities```, soit ```StubbedContext```. +``` +dotnet ef migrations add ex_042_015 --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_015_OneToMany_conventions**. + + * 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_015 --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_015_OneToMany_conventions.Albums.db* qui a été généré par l'exécution du +programme et qui se trouve près de *ex_042_015_OneToMany_conventions.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) + + diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/StubbedContext.cs b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/StubbedContext.cs new file mode 100644 index 0000000..332d02f --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/StubbedContext.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Text; + +namespace ex_042_016_OneToMany_FluentAPI +{ + 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().HasData(kindofblue, dialogue); + + modelBuilder.Entity().HasData(new { MorceauId = 1, AlbumForeignKey = 1, Titre = "So What" }, + new { MorceauId = 2, AlbumForeignKey = 1, Titre = "Freddie Freeloader" }, + new { MorceauId = 3, AlbumForeignKey = 1, Titre = "Blue in Green" }, + new { MorceauId = 4, AlbumForeignKey = 1, Titre = "All Blues" }, + new { MorceauId = 5, AlbumForeignKey = 1, Titre = "Flamenco Sketches" }, + new { MorceauId = 6, AlbumForeignKey = 2, Titre = "Catta" }, + new { MorceauId = 7, AlbumForeignKey = 2, Titre = "Idle While" }, + new { MorceauId = 8, AlbumForeignKey = 2, Titre = "Les Noirs Marchant" }, + new { MorceauId = 9, AlbumForeignKey = 2, Titre = "Dialogue" }, + new { MorceauId = 10, AlbumForeignKey = 2, Titre = "Ghetto Lights" }, + new { MorceauId = 11, AlbumForeignKey = 2, Titre = "Jasper" } + ); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ex_042_016_OneToMany_FluentAPI.csproj b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ex_042_016_OneToMany_FluentAPI.csproj new file mode 100644 index 0000000..47d4d62 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_016_OneToMany_FluentAPI/ex_042_016_OneToMany_FluentAPI.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.0 + $(MSBuildProjectDirectory) + + + + + + + + +