updated ex_042_012_EF_CF_Dictionary about dictionaries and EF

updatesEFSamplesToNetCore3
Marc CHEVALDONNE 6 years ago
parent 65edc7fa4e
commit eefe5b5e31

@ -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<Score> lesScores = new List<Score>();
//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;

@ -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
/// </summary>
public class NounoursDBEntities : DbContext
{
public virtual DbSet<NounoursEntity> NounoursSet { get; set; }
public virtual DbSet<Nounours> NounoursSet { get; set; }
public virtual DbSet<LitEntity> LitsSet { get; set; }
public virtual DbSet<Score> 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<NounoursEntity>().ToTable("Nounours"); //nom de la table
modelBuilder.Entity<NounoursEntity>().HasKey(n => n.UniqueId); //définition de la clé primaire
modelBuilder.Entity<NounoursEntity>().Property(n => n.UniqueId);
modelBuilder.Entity<NounoursEntity>().Property(n => n.Nom).IsRequired()
modelBuilder.Entity<Nounours>().ToTable("Nounours"); //nom de la table
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId); //définition de la clé primaire
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId)
.ValueGeneratedOnAdd() ;
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
.HasMaxLength(256); //définition de la colonne Nom
modelBuilder.Entity<NounoursEntity>().Property(n => n.DateDeNaissance).HasColumnName("Naissance"); //changement du nom de la colonne Naissance
//modelBuilder.Entity<NounoursEntity>().Property<Guid>("ForeignKey");
//modelBuilder.Entity<Nounours>().Property<Score>("Score");
modelBuilder.Entity<Nounours>().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<Lit>().ToTable("Lits"); // nom de la table
// modelBuilder.Entity<Lit>().HasKey(c => c.UniqueId); //définition de la clé primaire
// modelBuilder.Entity<Lit>().Property(c => c.UniqueId)
// .ValueGeneratedOnAdd();
// modelBuilder.Entity<Lit>().Property<IEnumerable<Score>>("ScoresList");
// //note : la colonne LastModified n'est pas touchée : utilisation des conventions EF
modelBuilder.Entity<Score>().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<Score>().Property<Guid>("ScoreId");
//on précise qu'il y a une relation entre Score et Nounours
modelBuilder.Entity<Score>() //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<NounoursEntity>(ne => ne.UniqueId);//dont la propriété UniqueId est une Foreign Key
//remplace la ForeignKey
modelBuilder.Entity<LitEntity>()
.HasMany("mScores")
.WithOne("Lit")
.HasForeignKey("UniqueId");
//on précise qu'il y a une relation entre Lit et Score
modelBuilder.Entity<LitEntity>().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);
}
/// <summary>
/// permet de récupérer les données rentrées en base en les projetant dans les types utilisés pour le Model.
/// </summary>
/// <param name="lits">les lits rentrés en base</param>
/// <param name="nounours">les nounours rentrés en base</param>
/// <returns>true si la récupération est réussie, false sinon</returns>
//public bool GetAll(out IEnumerable<ILit> lits, out IEnumerable<INounours> 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<Score> lesScores = new List<Score>();
/// <summary>
/// ajoute des lits et des nounours à la base
/// Pour cela, cette méthode projette les Lit et les Nounours en LitEx, NounoursEx et Score
/// </summary>
/// <param name="lesLits"></param>
/// <param name="lesNounours"></param>
public void AddAll(IEnumerable<LitEntity> lesLits, IEnumerable<NounoursEntity> lesNounours)
public void AddAll(IEnumerable<LitEntity> lesLits, IEnumerable<Nounours> 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();
/// <summary>
/// transforme les dictionnaires en collection de scores
/// </summary>
/// <param name="lit"></param>
//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;
// }
//}
}
}

@ -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
{
/// <summary>
/// classe permettant de réaliser une relation one to many avec Lit, et one to one avec Nounours
/// Elle remplace le KeyValuePair<Nounours, int>, type des éléments du dictionaire Scores de Lit.
/// </summary>
public class Score : Item<NounoursEntity, int>
public class Score : Item<Nounours, int>
{
/// <summary>
/// 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;
}
}
/// <summary>
/// classe de base permettant de transformer un dictionnaire Dictionary<TKey, TValue> en
/// ICollection<Item<TKey, TValue>> 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).
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class Item<TKey, TValue>
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UniqueId
{
get; set;
}
public TKey Key
{
get;
set;
}
public TValue Value
{
get; set;
}
}
}

@ -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
//
// ========================================================================

@ -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
//
// ========================================================================

@ -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<Nounours, int> 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<TKey, TValue>. 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<Nounours, int> 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)

Loading…
Cancel
Save