diff --git a/Exemples.sln b/Exemples.sln index 9d41e01..f1e324f 100644 --- a/Exemples.sln +++ b/Exemples.sln @@ -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} diff --git a/p08_BDD_EntityFramework/ReadMe.md b/p08_BDD_EntityFramework/ReadMe.md index beee7c3..2cc267a 100644 --- a/p08_BDD_EntityFramework/ReadMe.md +++ b/p08_BDD_EntityFramework/ReadMe.md @@ -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* : diff --git a/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations/ReadMe.md b/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations/ReadMe.md index 8d76642..d40688e 100644 --- a/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_014_OneToMany_dataAnnotations/ReadMe.md @@ -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 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``` diff --git a/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Album.cs b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Album.cs new file mode 100644 index 0000000..584b377 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Album.cs @@ -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 +{ + /// + /// 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. + /// + 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_015_OneToMany_conventions/AlbumDBEntities.cs b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/AlbumDBEntities.cs new file mode 100644 index 0000000..660948a --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/AlbumDBEntities.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; + +namespace ex_042_015_OneToMany_conventions +{ + /// + /// 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_015_OneToMany_conventions.Albums.db"); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Morceau.cs b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Morceau.cs new file mode 100644 index 0000000..30be103 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Morceau.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_015_OneToMany_conventions +{ + /// + /// 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. + /// + 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_015_OneToMany_conventions/Program.cs b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Program.cs new file mode 100644 index 0000000..674bac9 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/Program.cs @@ -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 + { + /// + /// 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_015_OneToMany_conventions/ReadMe.md b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/ReadMe.md new file mode 100644 index 0000000..0ef582a --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/ReadMe.md @@ -0,0 +1,348 @@ +# ex_042_015_OneToMany_conventions + +*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 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```. +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 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 Albums { get; set; } +public DbSet 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. + +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().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) + + diff --git a/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/StubbedContext.cs b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/StubbedContext.cs new file mode 100644 index 0000000..0417fc5 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/StubbedContext.cs @@ -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().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" } + ); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/ex_042_015_OneToMany_conventions.csproj b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/ex_042_015_OneToMany_conventions.csproj new file mode 100644 index 0000000..47d4d62 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/ex_042_015_OneToMany_conventions.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.0 + $(MSBuildProjectDirectory) + + + + + + + + + diff --git a/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/readme_files/ex_042_015_classDiagram2.svg b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/readme_files/ex_042_015_classDiagram2.svg new file mode 100644 index 0000000..2203941 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_015_OneToMany_conventions/readme_files/ex_042_015_classDiagram2.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + Album + + + + + + +AlbumId: int + + + + + + +Titre: string + + + + + + +DateDeSortie: DateTime + + + + + + + + + + + + + + + + + + + + + Morceau + + + + + + +MorceauId: int + + + + + + +Titre: string + + + + + + +AlbumAlbumId: int + + + + + + + + + +Album + + + + + + 1 + + + + + + +Morceaux + + + + + + * + + + + + + + + + + + + + + + ForeignKey : + AlbumAlbumId + + + + + + + \ No newline at end of file diff --git a/p08_BDD_EntityFramework/temp.md b/p08_BDD_EntityFramework/temp.md index 6ed213f..157bedc 100644 --- a/p08_BDD_EntityFramework/temp.md +++ b/p08_BDD_EntityFramework/temp.md @@ -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