// ========================================================================
//
// Copyright (C) 2019-2020 MARC CHEVALDONNE
//                         marc.chevaldonne.free.fr
//
// Module        : Program.cs
// Author        : Marc Chevaldonné
// Creation date : 2016-11-01
// Mise à jour   : 2019-12-25
//
// ========================================================================

using Microsoft.EntityFrameworkCore;
using static System.Console;

namespace ex_042_012_EF_CF_Dictionary
{
    /// <summary>
    /// 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.
    /// Son avantage est de ne pas vous obliger à modifier votre modèle. Pour cela :
    /// 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 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é.
    /// 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 
    /// dans Entity Framework Core. Espérons qu'il sera possible dans la prochaine version d'EF Core d'utiliser des shadow properties de type collections, ou mieux, que les dictionnaires soient gérés par EF Core et
    /// directement transformés en relations !
    /// </summary>
    public class Program
    {
        public static void Main(string[] args)
        {
            //création du DbContext et injection de la dépendance à MyStubDataInitializationStrategy
            using (NounoursDBEntities db = new NounoursDBEntities())
            {
                //choix de la stratégie et remplissage avec des données stubbées
                DbContextInitializer.Seed(db);
            }

            using (NounoursDBEntities db = new NounoursDBEntities())
            {
                WriteLine("les nounours : ");
                foreach (var n in db.NounoursSet)
                {
                    WriteLine($"\t{n}");
                }

                WriteLine("les lits : ");
                foreach (var l in db.LitsSet.Include(l => l.mScores))
                {
                    WriteLine($"\t{l.Propriétaire}");
                    foreach (var kvp in l.Scores)
                    {
                        WriteLine($"\t\t{kvp.Key.Nom}: {kvp.Value}");
                    }
                }
            }
        }
    }
}
