// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
//                         marc.chevaldonne.free.fr
//
// Module        : NounoursDBEntities.cs
// Author        : Marc Chevaldonné
// Creation date : 2016-10-12
//
// ========================================================================

using Microsoft.EntityFrameworkCore;

namespace ex_042_003_EF_CodeFirst_Fluent_API
{
    /// <summary>
    /// La classe qui dérive de DbContext est celle qui permettra de faire les opérations CRUD sur le modèle.
    /// Cette classe contient un DbSet<T> pour permettre de réaliser des opérations CRUD sur le type T, ici Nounours.
    /// 
    /// Lorsque nous utiliserons cette classe, le programme cherchera la chaîne de connection (connectionString) avec le même nom que celui passé
    ///  dans le constructeur de DbContext (ici NounoursDBContext). Il cherche la connectionString à différents endroits et en particulier ici 
    ///  dans App.config.
    /// Si cette table n'existe pas, il va la créer automatiquement. Ceci est configurable et sera présenté dans les exemples suivants.
    /// 
    /// Contrairement aux 2 exemples précédents, nous n'utilisons pas les conventions d'écriture, ni les annotations de données pour utiliser créer la table
    /// à partir de la classe Nounours. Nous allons cette fois-ci utiliser la Fluent API et réécrire dans la classe NounoursDBEntities dérivant de DbContext
    /// de quelle manière créer la table à partir de la classe Nounours.
    /// 
    /// Cette méthode est préférable dans les deux cas principaux suivants (et en particulier le 1er) :
    /// - nous n'avons pas accès au code source de la classe Nounours ou bien nous n'avons pas le droit de le modifier, et les conventions d'écriture ne 
    /// correspondent pas à ce que nous souhaitons réaliser.
    /// - nous ne souhaitons pas "polluer" la classe POCO avec des annotations de données.
    /// 
    /// L'inconvénient majeur et évident de cette méthode est que la lecture et la maintenance sont plus compliquées.
    /// 
    /// Pour utiliser la Fluent API et décrire de quelle manière créer la table à partir de la classe POCO, il faut réécrire la méthode virtuelle OnModelCreating
    /// (cf. commentaires de la méthode ci-dessous).
    /// 
    /// L'exemple ci-dessous recrée exactement la même table que dans l'exemple précédent.
    /// </summary>
    class NounoursDBEntities : DbContext
    {
        public virtual DbSet<Nounours> NounoursSet { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_003_EF_CodeFirst_Fluent_API.Nounours.mdf;Trusted_Connection=True;");
        }

        /// <summary>
        /// méthode appelée lors de la création du modèle. 
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //ici on précise comment s'appellera la table associée à la classe POCO Nounours
            //équivalent du [Table("TableNounours")] avec les annotations de données
            modelBuilder.Entity<Nounours>().ToTable("TableNounours");

            //ici on précise que la propriété UniqueId de Nounours est la clef primaire
            //équivalent de [Key] devant la propriété UniqueId dans Nounours
            modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);

            //ici on explique que c'est lors de l'insertion en base que la clef primaire sera générée
            //équivalent de [DatabaseGenerated(DatabaseGeneratedOption.Identity)] devant la propriété UniqueId de Nounours
            modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
            //HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);

            //ici on précise que la propriété Nom est obligatoire et que sa taille maximale est de 256 caractères
            //Notez l'utilisation des méthodes chaînées ! Trop beau ! Tellement pratique, intuitif et évident...
            //équivalent de [Required] et [MaxLength(256)] devant la propriété Nom de Nounours
            modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
                                                                .HasMaxLength(256);

            //ici on donne un nom à la colonne associée à la propriété DateDeNaissance
            //équivalent de [Column("Naissance")] devant la propriété DateDeNaissance de Nounours
            modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance");

            //ici on précise que la propriété NbPoils ne sera pas insérée en base 
            //équivalent de [NotMapped] devant la propriété NbPoils de Nounours
            modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);

            base.OnModelCreating(modelBuilder);
        }
    }
}
