modified Many to Many samples in Fluent API

updatesEFSamplesToNetCore3
Marc CHEVALDONNE 5 years ago
parent 01783aae1f
commit 504d0c6aa3

@ -0,0 +1,64 @@
// ========================================================================
//
// Copyright (C) 2019-2020 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Album.cs
// Author : Marc Chevaldonné
// Creation date : 2019-12-23
//
// ========================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
/// <summary>
/// Album est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation many-many avec la classe Artiste via la propriété Artistes.
/// La clé primaire est générée lors de l'insertion en table.
///
/// Aujourd'hui (21 octobre 2016), il n'est pas encore possible d'utiliser les annotations de données pour les relations many to many.
/// En conséquence, il faut créer une classe pour la table d'association.
/// Dans la classe Album, on trouve donc une propriété AlbumsArtistes, collection de AlbumArtiste (correspondant à la table d'association).
/// On trouve également une propriété calculée Artistes qui réalise une projection de la collection précédente pour obtenir les artistes, ainsi
/// qu'une méthode AddArtiste rajoutant une ligne dans la table d'association.
/// </summary>
public class Album
{
public Guid UniqueId
{
get; set;
}
public string Titre
{
get; set;
}
public DateTime DateDeSortie
{
get; set;
}
public virtual IEnumerable<Artiste> Artistes
{
get
{
return AlbumsArtistes.Select(aa => aa.Artiste);
}
}
public void AddArtiste(Artiste artiste)
{
AlbumsArtistes.Add(new AlbumArtiste() { Album = this, Artiste = artiste });
}
public virtual ICollection<AlbumArtiste> AlbumsArtistes { get; set; } = new List<AlbumArtiste>();
}
}

@ -0,0 +1,33 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : AlbumArtiste.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-21
//
// ========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
/// <summary>
/// table permettant de réaliser la table d'association
/// </summary>
public class AlbumArtiste
{
/// <summary>
/// un album...
/// </summary>
public Album Album { get; set; }
/// <summary>
/// ...lié à un artiste
/// </summary>
public Artiste Artiste { get; set; }
}
}

@ -0,0 +1,67 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : AlbumArtisteDBEntities.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-21
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
/// <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 deux DbSet<T> pour permettre de réaliser des opérations CRUD sur les types T, ici Album et Artiste.
/// </summary>
public class AlbumArtisteDBEntities : DbContext
{
public virtual DbSet<Artiste> Artistes { get; set; }
public virtual DbSet<Album> Albums { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_010_EF_CF_Many_to_Many.mdf;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//création de la table Albums
modelBuilder.Entity<Album>().ToTable("Albums"); //nom de la table
modelBuilder.Entity<Album>().HasKey(a => a.UniqueId); //définition de la clé primaire
modelBuilder.Entity<Album>().Property(a => a.UniqueId)
.ValueGeneratedOnAdd(); //définition du mode de génération de la clé : génération à l'insertion
//création de la table "Artistes"
modelBuilder.Entity<Artiste>().ToTable("Artistes"); // nom de la table
modelBuilder.Entity<Artiste>().HasKey(a => a.UniqueId); //définition de la clé primaire
modelBuilder.Entity<Artiste>().Property(a => a.UniqueId)
.ValueGeneratedOnAdd(); // définition du mode de génération de la clé : pas de génération automatique
//ajoute deux shadow properties à la classe AlbumArtiste pour qu'elles soient utilisées comme clés étrangères
modelBuilder.Entity<AlbumArtiste>().Property<Guid>("AlbumId");
modelBuilder.Entity<AlbumArtiste>().Property<Guid>("ArtisteId");
//crée une clé primaire à partir des deux propriétés précédentes
modelBuilder.Entity<AlbumArtiste>().HasKey("AlbumId", "ArtisteId");
//lie l'entité AlbumArtiste à l'entité Album
modelBuilder.Entity<AlbumArtiste>()
.HasOne(aa => aa.Album)
.WithMany(album => album.AlbumsArtistes)
.HasForeignKey("AlbumId");
//lie l'entité AlbumArtiste à l'entité Artiste
modelBuilder.Entity<AlbumArtiste>()
.HasOne(aa => aa.Artiste)
.WithMany(artiste => artiste.AlbumsArtistes)
.HasForeignKey("ArtisteId");
base.OnModelCreating(modelBuilder);
}
}
}

@ -0,0 +1,73 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : Artiste.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-21
//
// ========================================================================
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
/// <summary>
/// Artiste est une classe POCO, i.e. Plain Old CLR Object.
/// Elle a une relation many-many avec la classe Artiste via la propriété Artistes.
/// La clé primaire est générée lors de l'insertion en table.
///
/// Aujourd'hui (21 octobre 2016), il n'est pas encore possible d'utiliser les annotations de données pour les relations many to many.
/// En conséquence, il faut créer une classe pour la table d'association.
/// Dans la classe Artiste, on trouve donc une propriété AlbumsArtistes, collection de AlbumArtiste (correspondant à la table d'association).
/// On trouve également une propriété calculée Albums qui réalise une projection de la collection précédente pour obtenir les albums, ainsi
/// qu'une méthode AddAlbum rajoutant une ligne dans la table d'association.
/// </summary>
public class Artiste
{
public Guid UniqueId
{
get; set;
}
public string Prénom
{
get; set;
}
public string Nom
{
get; set;
}
public DateTime DateDeNaissance
{
get; set;
}
public DateTime? DateDeMort
{
get; set;
}
public virtual IEnumerable<Album> Albums
{
get
{
return AlbumsArtistes.Select(aa => aa.Album);
}
}
public void AddAlbum(Album album)
{
AlbumsArtistes.Add(new AlbumArtiste() { Album = album, Artiste = this });
}
public virtual ICollection<AlbumArtiste> AlbumsArtistes { get; set; } = new List<AlbumArtiste>();
}
}

@ -0,0 +1,105 @@
// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
// marc.chevaldonne.free.fr
//
// Module : DbContextInitializer.cs
// Author : Marc Chevaldonné
// Creation date : 2016-10-21
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
/// <summary>
/// initialiseur de stratégies...
/// </summary>
public static class DbContextInitializer
{
/// <summary>
/// remplissage de la base avec des données stubbées.
/// </summary>
/// <param name="context">base à remplir</param>
public static void Seed(AlbumArtisteDBEntities context)
{
SetInitializer(context, InitializationStrategies.DropCreateDatabaseAlways);
Dictionary<string, Album> albums = new Dictionary<string, Album>()
{
["kindOfBlue"] = new Album { Titre = "Kind of Blue", DateDeSortie = new DateTime(1959, 8, 17) },
["somethinElse"] = new Album { Titre = "Somethin' Else", DateDeSortie = new DateTime(1958, 8, 1) }
};
Dictionary<string, Artiste> artistes = new Dictionary<string, Artiste>()
{
["milesDavis"] = new Artiste { Prénom = "Miles", Nom = "Davis", DateDeNaissance = new DateTime(1926, 5, 26), DateDeMort = new DateTime(1991, 9, 28) },
["johnColtrane"] = new Artiste { Prénom = "John", Nom = "Coltrane", DateDeNaissance = new DateTime(1926, 9, 23), DateDeMort = new DateTime(1967, 7, 11) },
["julianAdderley"] = new Artiste { Prénom = "Julian", Nom = "Adderley", DateDeNaissance = new DateTime(1928, 9, 15), DateDeMort = new DateTime(1975, 8, 8) },
["billEvans"] = new Artiste { Prénom = "Bill", Nom = "Evans", DateDeNaissance = new DateTime(1929, 8, 16), DateDeMort = new DateTime(1980, 9, 15) },
["wyntonKelly"] = new Artiste { Prénom = "Wynton", Nom = "Kelly", DateDeNaissance = new DateTime(1931, 12, 2), DateDeMort = new DateTime(1971, 4, 12) },
["paulChambers"] = new Artiste { Prénom = "Paul", Nom = "Chambers", DateDeNaissance = new DateTime(1935, 4, 22), DateDeMort = new DateTime(1969, 1, 4) },
["jimmyCobb"] = new Artiste { Prénom = "Jimmy", Nom = "Cobb", DateDeNaissance = new DateTime(1929, 1, 20) },
["hankJones"] = new Artiste { Prénom = "Hank", Nom = "Jones", DateDeNaissance = new DateTime(1918, 7, 31), DateDeMort = new DateTime(2010, 5, 16) },
["samJones"] = new Artiste { Prénom = "Sam", Nom = "Jones", DateDeNaissance = new DateTime(1924, 11, 12), DateDeMort = new DateTime(1981, 12, 15) },
["artBlakey"] = new Artiste { Prénom = "Art", Nom = "Blakey", DateDeNaissance = new DateTime(1919, 10, 11), DateDeMort = new DateTime(1990, 10, 16) }
};
//les artistes qui jouent sur Kind Of Blue sont reliés à l'album
albums["kindOfBlue"].AddArtiste(artistes["milesDavis"]);
albums["kindOfBlue"].AddArtiste(artistes["johnColtrane"]);
albums["kindOfBlue"].AddArtiste(artistes["julianAdderley"]);
albums["kindOfBlue"].AddArtiste(artistes["billEvans"]);
albums["kindOfBlue"].AddArtiste(artistes["wyntonKelly"]);
albums["kindOfBlue"].AddArtiste(artistes["paulChambers"]);
albums["kindOfBlue"].AddArtiste(artistes["jimmyCobb"]);
//les artistes qui jouent sur Somethin' Else sont reliés à l'album
albums["somethinElse"].AddArtiste(artistes["julianAdderley"]);
albums["somethinElse"].AddArtiste(artistes["milesDavis"]);
albums["somethinElse"].AddArtiste(artistes["hankJones"]);
albums["somethinElse"].AddArtiste(artistes["samJones"]);
albums["somethinElse"].AddArtiste(artistes["artBlakey"]);
context.Albums.AddRange(albums.Values);
context.Artistes.AddRange(artistes.Values);
context.SaveChanges();
}
/// <summary>
/// les différentes stratégies de création de la base
/// </summary>
public enum InitializationStrategies
{
CreateDatabaseIfNotExists,
DropCreateDatabaseIfModelChanges,
DropCreateDatabaseAlways
}
public static void SetInitializer(DbContext context, InitializationStrategies strategy)
{
switch (strategy)
{
//par défaut : crée la base seulement si elle n'existe pas
default:
case InitializationStrategies.CreateDatabaseIfNotExists:
context.Database.EnsureCreated();
break;
//recrée la base même si elle existe déjà
case InitializationStrategies.DropCreateDatabaseAlways:
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
break;
//recrée la base seulement si le modèle change : impossible aujourd'hui en Entity Framework Core...
case InitializationStrategies.DropCreateDatabaseIfModelChanges:
throw new NotImplementedException("Le mode DropCreateDatabaseIfModelChanges ne peut pas encore exister sous Entity Framework Core");
}
}
}
}

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static System.Console;
namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
public class Program
{
/// <summary>
/// Cet exemple montre comment construire une relation many-many dans la base de données en utilisant les conventions de nommage Entity Framework.
///
/// Aujourd'hui (21 octobre 2016), il n'est malheureusement toujours pas possible d'utiliser les annotations de données pour réaliser une opération many to many.
/// Cet exemple propose une solution pour s'en approcher, mais qui nécessite quand même de modifier AlbumArtisteDBEntities (méthode OnModelCreating) et surtout
/// de créer une classe supplémentaire qui sera mappée à une table d'association.
/// Je ne conseille donc pas vraiment cette solution qui nécessite la modification des classes du modèle.
///
/// on utilise les données stubbées de DbContextInitializer
/// On affiche les Albums et les Artistes.
///
/// Si vous ouvrez la base de données (via l'explorateur d'objets SQL Server), vous pourrez constater la création d'une table d'association.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
try
{
//création du DbContext et injection de la dépendance à MyStubDataInitializationStrategy
using (AlbumArtisteDBEntities db = new AlbumArtisteDBEntities())
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
WriteLine("Albums : ");
foreach (var album in db.Albums)
{
WriteLine($"\t{album.UniqueId}: {album.Titre} (sorti le : {album.DateDeSortie.ToString("d")})");
foreach (var artiste in album.Artistes)
{
WriteLine($"\t\t{artiste.Prénom} {artiste.Nom}");
}
}
WriteLine();
WriteLine("Artistes :");
foreach (var artiste in db.Artistes)
{
var annéeDeMort = artiste.DateDeMort.HasValue ? $" - {artiste.DateDeMort.Value.Year}" : "";
var titresAlbums = artiste.Albums.Aggregate(String.Empty, (albums, album) => albums + $"\"{album.Titre}\" ");
WriteLine($"\t{artiste.UniqueId}: {artiste.Prénom} {artiste.Nom} ({artiste.DateDeNaissance.Year}{annéeDeMort}) (albums: {titresAlbums})");
}
}
}
catch (NotImplementedException exception)
{
WriteLine(exception.Message);
}
catch (Exception)
{
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 suivantes (que vous retrouvez en commentaires dans la classe Program) :");
WriteLine("Pour créer la base, ouvrez un invite de commandes et placez-vous dans le dossier de ce projet, ou bien,");
WriteLine("- dans Visual Studio ouvrez la Console du Gestionnaire de package (Outils -> Gestionnaire de package NuGet -> Console du Gestionnaire de package),");
WriteLine("- dans cette Console, vous devriez être dans le dossier de la solution, déplacez-vous dans celui du projet (ici : cd ex_042_010_EF_CF_Many_to_Many)");
WriteLine("- tapez : dotnet restore (pour restaurer les packages .NET Core)");
WriteLine("- tapez : dotnet ef migrations add MyFirstMigration");
WriteLine(" note : vous pourrez détruire le dossier Migrations une fois la base créée");
WriteLine("- tapez : dotnet ef database update");
WriteLine(" Ceci génère la base de données en utilisant la migration, et en particulier votre classe DBContext et vos classes POCO.");
WriteLine("\nDans cet exemple, une base de données SQLServer est créée et en particulier la table Nounours.mdf");
}
ReadLine();
}
}
}

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
Loading…
Cancel
Save