diff --git a/ex_042_012_EF_CF_Dictionary/DbEntities/DbContextInitializer.cs b/ex_042_012_EF_CF_Dictionary/DbEntities/DbContextInitializer.cs index 26bddbb..63a6eca 100644 --- a/ex_042_012_EF_CF_Dictionary/DbEntities/DbContextInitializer.cs +++ b/ex_042_012_EF_CF_Dictionary/DbEntities/DbContextInitializer.cs @@ -1,11 +1,12 @@ // ======================================================================== // -// Copyright (C) 2016-2017 MARC CHEVALDONNE +// Copyright (C) 2019-2020 MARC CHEVALDONNE // marc.chevaldonne.free.fr // // Module : DbContextInitializer.cs // Author : Marc Chevaldonné // Creation date : 2016-11-01 +// Last Modified : 2019-12-25 // // ======================================================================== @@ -28,19 +29,19 @@ namespace ex_042_012_EF_CF_Dictionary { SetInitializer(context, InitializationStrategies.DropCreateDatabaseAlways); - NounoursEntity[] lesNounours; + Nounours[] lesNounours; LitEntity[] lesLits; List lesScores = new List(); //données stubbées //simulation de données en utilisant les types du Model - NounoursEntity chewie = new NounoursEntity { Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 }; - NounoursEntity yoda = new NounoursEntity { Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 }; - NounoursEntity ewok = new NounoursEntity { Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }; - NounoursEntity beluga = new NounoursEntity { Nom = "Beluga", DateDeNaissance = new DateTime(2012, 07, 29), NbPoils = 0 }; - NounoursEntity singe = new NounoursEntity { Nom = "Singe", DateDeNaissance = new DateTime(2009, 08, 09), NbPoils = 1345 }; - NounoursEntity girafe = new NounoursEntity { Nom = "Girafe", DateDeNaissance = new DateTime(2007, 11, 02), NbPoils = 567 }; - lesNounours = new NounoursEntity[] { chewie, yoda, ewok, beluga, singe, girafe }; + Nounours chewie = new Nounours { Nom = "Chewbacca", DateDeNaissance = new DateTime(1977, 5, 27), NbPoils = 1234567 }; + Nounours yoda = new Nounours { Nom = "Yoda", DateDeNaissance = new DateTime(1980, 5, 21), NbPoils = 3 }; + Nounours ewok = new Nounours { Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 }; + Nounours beluga = new Nounours { Nom = "Beluga", DateDeNaissance = new DateTime(2012, 07, 29), NbPoils = 0 }; + Nounours singe = new Nounours { Nom = "Singe", DateDeNaissance = new DateTime(2009, 08, 09), NbPoils = 1345 }; + Nounours girafe = new Nounours { Nom = "Girafe", DateDeNaissance = new DateTime(2007, 11, 02), NbPoils = 567 }; + lesNounours = new Nounours[] { chewie, yoda, ewok, beluga, singe, girafe }; LitEntity litDeLuke = new LitEntity() { Propriétaire = "Luke" }; litDeLuke[chewie] = 100; diff --git a/ex_042_012_EF_CF_Dictionary/DbEntities/NounoursDbEntities.cs b/ex_042_012_EF_CF_Dictionary/DbEntities/NounoursDbEntities.cs index 0104e0d..aac8155 100644 --- a/ex_042_012_EF_CF_Dictionary/DbEntities/NounoursDbEntities.cs +++ b/ex_042_012_EF_CF_Dictionary/DbEntities/NounoursDbEntities.cs @@ -1,10 +1,17 @@ -using Microsoft.EntityFrameworkCore; +// ======================================================================== +// +// Copyright (C) 2019-2020 MARC CHEVALDONNE +// marc.chevaldonne.free.fr +// +// Module : LitEntity.cs +// Author : Marc Chevaldonné +// Last Modified : 2019-12-25 +// +// ======================================================================== + +using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; -using JetBrains.Annotations; -using System.Linq; -using System.Diagnostics; -using ex_042_012_EF_CF_Dictionary.Model; namespace ex_042_012_EF_CF_Dictionary { @@ -14,12 +21,13 @@ namespace ex_042_012_EF_CF_Dictionary /// public class NounoursDBEntities : DbContext { - public virtual DbSet NounoursSet { get; set; } + public virtual DbSet NounoursSet { get; set; } public virtual DbSet LitsSet { get; set; } public virtual DbSet ScoresSet { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + optionsBuilder.EnableSensitiveDataLogging(); optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_012_EF_CF_Dictionary.Nounours.mdf;Trusted_Connection=True;"); } @@ -30,99 +38,39 @@ namespace ex_042_012_EF_CF_Dictionary protected override void OnModelCreating(ModelBuilder modelBuilder) { //création de la table TableNounours - modelBuilder.Entity().ToTable("Nounours"); //nom de la table - modelBuilder.Entity().HasKey(n => n.UniqueId); //définition de la clé primaire - modelBuilder.Entity().Property(n => n.UniqueId); - modelBuilder.Entity().Property(n => n.Nom).IsRequired() + modelBuilder.Entity().ToTable("Nounours"); //nom de la table + modelBuilder.Entity().HasKey(n => n.UniqueId); //définition de la clé primaire + modelBuilder.Entity().Property(n => n.UniqueId) + .ValueGeneratedOnAdd() ; + modelBuilder.Entity().Property(n => n.Nom).IsRequired() .HasMaxLength(256); //définition de la colonne Nom - modelBuilder.Entity().Property(n => n.DateDeNaissance).HasColumnName("Naissance"); //changement du nom de la colonne Naissance - //modelBuilder.Entity().Property("ForeignKey"); - //modelBuilder.Entity().Property("Score"); + modelBuilder.Entity().Property(n => n.DateDeNaissance).HasColumnName("Naissance"); //changement du nom de la colonne Naissance //note : la colonne NbPoils n'est pas changée : utilisation des conventions EF - // //création de la table "Lits" - // modelBuilder.Entity().ToTable("Lits"); // nom de la table - // modelBuilder.Entity().HasKey(c => c.UniqueId); //définition de la clé primaire - // modelBuilder.Entity().Property(c => c.UniqueId) - // .ValueGeneratedOnAdd(); - // modelBuilder.Entity().Property>("ScoresList"); - // //note : la colonne LastModified n'est pas touchée : utilisation des conventions EF + modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); + //on crée une propriété fantôme (shadow property) pour l'entity Score qu'on utilisera comme clé étrangère (foreign key) + // En effet, j'ai pris le cas où nous décidons d'utiliser une classe existante pour laquelle aucune clé étrangère n'a été prévue. + // Il me semblait donc logique qu'on cherche à l'utiliser sans qu'elle soit dans le modèle + modelBuilder.Entity().Property("ScoreId"); - //on précise qu'il y a une relation entre Score et Nounours - modelBuilder.Entity() //l'entité Nounours... - .HasOne(s => s.Key) //a une propriété obligatoire Carnet... - .WithOne(ne => ne.Score) //reliée à la propriété Owner du Carnet... - .HasForeignKey(ne => ne.UniqueId);//dont la propriété UniqueId est une Foreign Key - //remplace la ForeignKey - - modelBuilder.Entity() - .HasMany("mScores") - .WithOne("Lit") - .HasForeignKey("UniqueId"); + //on précise qu'il y a une relation entre Lit et Score + modelBuilder.Entity().HasMany(l => l.mScores) // on dit que le lit a plusieurs scores + .WithOne(s => s.Lit) // et que chaque score a un lit + .HasForeignKey("ScoreId"); //on utilise la clé étrangère fantôme créée précédemment base.OnModelCreating(modelBuilder); } - /// - /// permet de récupérer les données rentrées en base en les projetant dans les types utilisés pour le Model. - /// - /// les lits rentrés en base - /// les nounours rentrés en base - /// true si la récupération est réussie, false sinon - //public bool GetAll(out IEnumerable lits, out IEnumerable nounours) - //{ - // try - // { - // // récupère la collection de NounoursEx contenus dans la base et les projette en collection de Nounours - // nounours = NounoursSet.ToList();//.Select(nEx => nEx.Nounours); - // //récupère la collection de LitEx contenus dans la base et les projette en collection de Lit - // lits = LitsSet.ToList(); - - // //parcourt la collection des scores contenus en base... - // foreach (Score score in ScoresSet) - // { - // //...pour chaque score ... - // //... trouve le lit concerné par ce score... - // ILit leLit = lits.Single(l => l.Propriétaire == score.Lit.Propriétaire); - // //...trouve le nounours concerné par ce score... - // INounours leNounours = nounours.Single(n => n.Equals(score.Key)); - // //... met à jour le dictionnaire dans l'instance de Lit - // leLit[leNounours] = score.Value; - // } - // return true; - // } - // catch (Exception e) - // { - // Debug.WriteLine(e.Message); - // lits = null; - // nounours = null; - // return false; - // } - //} - - //INounours[] lesNounours; - //ILit[] lesLits; List lesScores = new List(); /// /// ajoute des lits et des nounours à la base - /// Pour cela, cette méthode projette les Lit et les Nounours en LitEx, NounoursEx et Score /// /// /// - public void AddAll(IEnumerable lesLits, IEnumerable lesNounours) + public void AddAll(IEnumerable lesLits, IEnumerable lesNounours) { - // //projection des données dans les types NounoursEx, LitEx et Scores pour permettre d'utiliser EntityFramework pour l'insertion en base - // //lesNounoursEx = lesNounours.Select(n => new NounoursEx(n)).ToArray(); - // //lesLitsEx = lesLits.Select(l => new LitEx { Lit = l }).ToArray(); - - // //met à jour la collection de scores - // foreach (var litEx in lesLitsEx) - // { - // UpdateScores(litEx); - // } - //met à jour les tables NounoursSet.AddRange(lesNounours); LitsSet.AddRange(lesLits); @@ -130,28 +78,5 @@ namespace ex_042_012_EF_CF_Dictionary SaveChanges(); } - - //private INounoursEqualityComparer NounoursEqComparer { get; set; } = new INounoursEqualityComparer(); - - /// - /// transforme les dictionnaires en collection de scores - /// - /// - //private void UpdateScores(ILit lit) - //{ - // foreach (var kvp in lit.Scores) - // { - // NounoursEx nounoursEx = lesNounoursEx.Single(nEx => NounoursEqComparer.Equals(nEx, kvp.Key)); - // Score score = new Score() - // { - // Key = nounoursEx, - // Value = kvp.Value, - // Lit = litEx - // }; - // lesScores.Add(score); - // litEx.Scores.Add(score); - // nounoursEx.Score = score; - // } - //} } } diff --git a/ex_042_012_EF_CF_Dictionary/DbEntities/Score.cs b/ex_042_012_EF_CF_Dictionary/DbEntities/Score.cs index 67c7ef6..c35a586 100644 --- a/ex_042_012_EF_CF_Dictionary/DbEntities/Score.cs +++ b/ex_042_012_EF_CF_Dictionary/DbEntities/Score.cs @@ -1,25 +1,22 @@ // ======================================================================== // -// Copyright (C) 2016-2017 MARC CHEVALDONNE +// Copyright (C) 2019-2020 MARC CHEVALDONNE // marc.chevaldonne.free.fr // // Module : Score.cs // Author : Marc Chevaldonné // Creation date : 2016-11-01 +// Last Modified : 2019-12-25 // // ======================================================================== -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - namespace ex_042_012_EF_CF_Dictionary { /// /// classe permettant de réaliser une relation one to many avec Lit, et one to one avec Nounours /// Elle remplace le KeyValuePair, type des éléments du dictionaire Scores de Lit. /// - public class Score : Item + public class Score : Item { /// /// lien vers le LitEx pour la liaison one to many avec LitEx @@ -29,37 +26,4 @@ namespace ex_042_012_EF_CF_Dictionary get; set; } } - - - /// - /// classe de base permettant de transformer un dictionnaire Dictionary en - /// ICollection> permettant la liaison avec Entity Framwork. - /// Dans la classe Score, TKey est de type Nounours et TValue est de type int. - /// La propriété Key permet donc de réaliser la relation one to one avec NounoursEx. - /// La propriété Value permet d'obtenir la valeur associée (ici le score entier). - /// - /// - /// - public class Item - { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid UniqueId - { - get; set; - } - - public TKey Key - { - get; - set; - } - - public TValue Value - { - get; set; - } - } - - } diff --git a/ex_042_012_EF_CF_Dictionary/Model/Lit.cs b/ex_042_012_EF_CF_Dictionary/Model/Lit.cs index 86449a1..a9f4cb5 100644 --- a/ex_042_012_EF_CF_Dictionary/Model/Lit.cs +++ b/ex_042_012_EF_CF_Dictionary/Model/Lit.cs @@ -1,11 +1,12 @@ // ======================================================================== // -// Copyright (C) 2016-2017 MARC CHEVALDONNE +// Copyright (C) 2019-2020 MARC CHEVALDONNE // marc.chevaldonne.free.fr // // Module : Lit.cs // Author : Marc Chevaldonné // Creation date : 2016-11-01 +// Last Modified : 2019-12-25 // // ======================================================================== diff --git a/ex_042_012_EF_CF_Dictionary/Model/Nounours.cs b/ex_042_012_EF_CF_Dictionary/Model/Nounours.cs index 6297c6f..590330e 100644 --- a/ex_042_012_EF_CF_Dictionary/Model/Nounours.cs +++ b/ex_042_012_EF_CF_Dictionary/Model/Nounours.cs @@ -1,11 +1,12 @@ // ======================================================================== // -// Copyright (C) 2016-2017 MARC CHEVALDONNE +// Copyright (C) 2019-2020 MARC CHEVALDONNE // marc.chevaldonne.free.fr // // Module : Nounours.cs // Author : Marc Chevaldonné // Creation date : 2016-11-01 +// Last Modified : 2019-12-25 // // ======================================================================== diff --git a/ex_042_012_EF_CF_Dictionary/Program.cs b/ex_042_012_EF_CF_Dictionary/Program.cs index 249a618..9590478 100644 --- a/ex_042_012_EF_CF_Dictionary/Program.cs +++ b/ex_042_012_EF_CF_Dictionary/Program.cs @@ -1,17 +1,16 @@ // ======================================================================== // -// Copyright (C) 2016-2017 MARC CHEVALDONNE +// Copyright (C) 2019-2020 MARC CHEVALDONNE // marc.chevaldonne.free.fr // // Module : Program.cs // Author : Marc Chevaldonné // Creation date : 2016-11-01 -// Mise à jour : 2017-09-18 +// Mise à jour : 2019-12-25 // // ======================================================================== -using System; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; using static System.Console; namespace ex_042_012_EF_CF_Dictionary @@ -20,15 +19,19 @@ namespace ex_042_012_EF_CF_Dictionary /// L'utilisation de dictionnaires est "déconseillée" lors de l'utilisation d'Entity Framework par les utilisateurs car ils ne sont pas gérés par EF comme une relation entre entités. /// Personnellement, je n'aime pas que le choix d'une technologie apporte des contraintes sur mon modèle. /// Voici donc une proposition de solution quant à l'utilisation de dictionnaires dans votre modèle. - /// Elle est certes moins efficace que si vous n'utilisez que des collections car elle nécessite des projections. /// Son avantage est de ne pas vous obliger à modifier votre modèle. Pour cela : - /// 1) j'utilise deux nouvelles classes : NounoursEx et LitEx qui sont des procurateurs sur Nounours et Lit, - /// 2) le dictionnaire Dictionary dans Lit est transformé en deux relations : + /// 1) j'utilise deux interfaces publiques : ILit et INounours qui doivent être utilisées le plus possible en lieu et place des classes qui les implémentent. + /// 2) Nounours implémente INounours et comme c'est une classe simple (sans collection ni dictionnaire, ni lien direct avec une autre classe), elle sera également utilisée + /// comme entité via EF ; mais comme je n'ai pas voulu polluer mon modèle avec des annotations EF, j'utilise soit les conventions de nommage, soit la fluent API (cf. NounoursDbEntities.cs) + /// 3) Lit implémente ILit avec un dictionnaire, mais on n'accède jamais au dictionnaire directement (imposé par ILit). On peut lire et modifier les élements via le ReadOnlyDictionary, + /// et l'indexeur. Lit ne peut pas être utilisé comme entité à cause du dictionnaire. Je crée donc une autre classe LitEntity qui implémente ILit et permet le lien avec EF. + /// Ici, le dictionnaire est remplacé par une collection de Score qui implémente Item. Une projection permet de la transformer en ReadOnlyDictionary, et l'indexeur accède aux éléments de la collection. + /// Puisque LitEntity est écrite pour le lien avec EF, je peux me permettre d'utiliser les annotations et la Fluent API pour le lien avec les scores. + /// En conséquence, le dictionnaire ReadOnlyDictionary dans ILit est transformé en deux relations : /// a) une relation one to many entre Lit et une nouvelle classe Score - /// b) une relation one to one entre Score et Nounours + /// b) une relation entre Score et Nounours /// De plus, Score contient non seulement le Nounours qui servait de clé dans le dictionnaire, mais également la valeur qui était associée à cette clé. - /// 3) la classe NounoursDBEntities (le DBContext) possède deux méthodes GetAll et AddAll permettant respectivement de récupérer des Nounours et des Lits à partir des entités de NounoursEx, LitEx et Score, - /// et d'ajouter dans la base des nounours et des lits en les projetant en entités de types NounoursEx, LitEx et Score. + /// 4) la classe NounoursDBEntities (le DBContext) possède une méthode AddAll permettant d'ajouter dans la base des nounours et des lits. /// /// Pour finir, j'ai cherché à mettre en oeuvre une solution plus élégante et plus rapide à écrire avec Entity Framework 7. Celle-ci se base sur l'utilisation des shadow properties et de la réécriture des méthodes /// du DbContext pour permettre la transformation du dictionnaire en collections. Malheureusement, cette solution ne peut pas être utilisée aujourd'hui car les shadow properties ne peuvent pas être des collections @@ -55,7 +58,7 @@ namespace ex_042_012_EF_CF_Dictionary } WriteLine("les lits : "); - foreach (var l in db.LitsSet) + foreach (var l in db.LitsSet.Include(l => l.mScores)) { WriteLine($"\t{l.Propriétaire}"); foreach (var kvp in l.Scores)