added sample ex_042_004_Keys_conventions

EFCore3_Reforged
Marc CHEVALDONNE 5 years ago
parent 54de608c6e
commit de33f882a7

@ -562,7 +562,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_ValueGeneration_
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_005_ValueGeneration_data_annotations", "p08_BDD_EntityFramework\ex_042_005_ValueGeneration_data_annotations\ex_042_005_ValueGeneration_data_annotations.csproj", "{87EF3DAF-4166-496F-B0CE-546E20FADBAC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_006_ValueGeneration_FluentAPI", "p08_BDD_EntityFramework\ex_042_006_ValueGeneration_FluentAPI\ex_042_006_ValueGeneration_FluentAPI.csproj", "{BAB08578-898E-48C5-8470-6AC72D49B0D8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_006_ValueGeneration_FluentAPI", "p08_BDD_EntityFramework\ex_042_006_ValueGeneration_FluentAPI\ex_042_006_ValueGeneration_FluentAPI.csproj", "{BAB08578-898E-48C5-8470-6AC72D49B0D8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_004_Keys_conventions", "p08_BDD_EntityFramework\ex_042_004_Keys_conventions\ex_042_004_Keys_conventions.csproj", "{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -4680,6 +4682,26 @@ Global
{BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x64.Build.0 = Release|Any CPU
{BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x86.ActiveCfg = Release|Any CPU
{BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x86.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|ARM64.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x64.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x64.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x86.ActiveCfg = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Debug|x86.Build.0 = Debug|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|Any CPU.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM64.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|ARM64.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x64.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x64.Build.0 = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x86.ActiveCfg = Release|Any CPU
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -4947,6 +4969,7 @@ Global
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{87EF3DAF-4166-496F-B0CE-546E20FADBAC} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{BAB08578-898E-48C5-8470-6AC72D49B0D8} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
{DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E}

@ -9,6 +9,7 @@
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
using static System.Console;
@ -33,9 +34,11 @@ namespace ex_042_006_EF_CF_One_to_One
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
}
using (NounoursDBEntities db = new NounoursDBEntities())
{
WriteLine("nounours : ");
foreach (var n in db.NounoursSet)
foreach (var n in db.NounoursSet.Include(n => n.Carnet))
{
WriteLine($"\t{n}, LastModified: {n.Carnet.LastModified.ToString("d")}");
}

@ -9,6 +9,7 @@
//
// ========================================================================
using Microsoft.EntityFrameworkCore;
using System;
using static System.Console;
@ -38,9 +39,11 @@ namespace ex_042_008_EF_CF_One_to_Many
{
//choix de la stratégie et remplissage avec des données stubbées
DbContextInitializer.Seed(db);
}
using (AlbumDBEntities db = new AlbumDBEntities())
{
WriteLine("Albums : ");
foreach (var a in db.Albums)
foreach (var a in db.Albums.Include(a => a.Morceaux))
{
WriteLine($"\t{a.UniqueId}: {a.Titre} (sorti le : {a.DateDeSortie.ToString("d")})");
foreach (var m in a.Morceaux)

@ -51,12 +51,12 @@ namespace ex_042_009_EF_CF_One_to_Many_FluentAPI
//on crée une propriété fantôme (shadow property) pour l'entity Morceau 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<Morceau>().Property<Guid>("MorceauId");
modelBuilder.Entity<Morceau>().Property<Guid>("AlbumId");
//on précise qu'il y a une relation entre Album et Morceau
modelBuilder.Entity<Album>().HasMany(a => a.Morceaux) // on dit que l'Album a plusieurs morceaux
.WithOne(m => m.Album) // et que chaque morceau a un album
.HasForeignKey("MorceauId"); //on utilise la clé étrangère fantôme créée précédemment
.HasForeignKey("AlbumId"); //on utilise la clé étrangère fantôme créée précédemment
//Si vous regardez la table obtenue, vous pouvez voir qu'Entity Framework a automatiquement généré une clé étrangère.

@ -39,7 +39,8 @@ namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
{
WriteLine("Albums : ");
foreach (var album in db.Albums.Include(a => a.AlbumsArtistes).ThenInclude(aa => aa.Artiste))
foreach (var album in db.Albums.Include(a => a.AlbumsArtistes)
.ThenInclude(aa => aa.Artiste))
{
WriteLine($"\t{album.UniqueId}: {album.Titre} (sorti le : {album.DateDeSortie.ToString("d")})");
foreach (var artiste in album.Artistes)
@ -51,7 +52,8 @@ namespace ex_042_011_EF_CF_Many_to_Many_FluentAPI
WriteLine();
WriteLine("Artistes :");
foreach (var artiste in db.Artistes.Include(a => a.AlbumsArtistes).ThenInclude(aa => aa.Album))
foreach (var artiste in db.Artistes.Include(a => a.AlbumsArtistes)
.ThenInclude(aa => aa.Album))
{
var annéeDeMort = artiste.DateDeMort.HasValue ? $" - {artiste.DateDeMort.Value.Year}" : "";
var titresAlbums = artiste.Albums.Aggregate(String.Empty, (albums, album) => albums + $"\"{album.Titre}\" ");

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ex_042_004_Keys_conventions
{
public class Cylon
{
public int CylonId
{
get; set;
}
public string Name
{
get; set;
}
public int Generation
{
get; set;
}
public override string ToString()
{
return $"Cylon {CylonId}: {Name}, Number {Generation}";
}
}
}

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_004_Keys_conventions
{
class DBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
public virtual DbSet<Cylon> CylonsSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_004_Keys_conventions.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,81 @@
using System;
namespace ex_042_004_Keys_conventions
{
public class Nounours
{
public int ID
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"Nounours {ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,79 @@
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_004_Keys_conventions
{
class Program
{
static void Main(string[] args)
{
OutputEncoding = System.Text.Encoding.UTF8;
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 };
Cylon c1 = new Cylon { Name = "John Cavil", Generation = 1 };
Cylon c2 = new Cylon { Name = "Leoben Conoy", Generation = 2 };
Cylon c3 = new Cylon { Name = "D'Anna Biers", Generation = 3 };
Cylon c4 = new Cylon { Name = "Simon", Generation = 4 };
Cylon c5 = new Cylon { Name = "Aaron Doral", Generation = 5 };
Cylon c6 = new Cylon { Name = "Caprica 6", Generation = 6 };
Cylon c7 = new Cylon { Name = "Daniel", Generation = 7 };
Cylon c8 = new Cylon { Name = "Boomer", Generation = 8 };
Cylon c9 = new Cylon { Name = "Athena", Generation = 8 };
try
{
using (DBEntities db = new DBEntities())
{
//nettoyage de la base de données
if (db.NounoursSet.Count() > 0)
{
foreach (var n in db.NounoursSet)
{
db.NounoursSet.Remove(n);
}
db.SaveChanges();
}
if (db.CylonsSet.Count() > 0)
{
foreach (var c in db.CylonsSet)
{
db.CylonsSet.Remove(c);
}
db.SaveChanges();
}
//ajout des nounours dans la base de données
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.CylonsSet.AddRange(new Cylon[] { c1, c2, c3, c4, c5, c6, c7, c8, c9 });
db.SaveChanges();
}
using (DBEntities db = new DBEntities())
{
WriteLine("database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
foreach (var c in db.CylonsSet)
{
WriteLine($"\t{c}");
}
}
}
catch (SqlException)
{
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 données dans le fichier ReadMe.md associé à cet exemple.");
}
ReadLine();
}
}
}

@ -0,0 +1,113 @@
# ex_042_004_Keys_conventions
*06/01/2020 &sdot; Marc Chevaldonné*
---
Cet exemple traite des clés primaires associées aux entités.
Prérequis : je n'explique pas à travers cet exemple les principes de base d'**Entity Framework Core** et en particulier les chaînes de connexion et le lien entre entité et table.
Pour plus de renseignements sur :
* les chaînes de connexion : *ex_041_001_ConnectionStrings*
* les liens entre entités et tables : *ex_042_001_EF_CF_conventions*, *ex_042_002_EF_CF_data_annotations* et *ex_042_003_EF_CF_Fluent_API*
Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation des **conventions d'écriture**.
Vous pourrez trouver une version plus ou moins équivalente avec les *data annotations* ici : **ex_042_005_Keys_data_annotations**.
Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : **ex_042_006_Keys_FluentAPI**.
---
## Les clés primaires
Une clé permet de rendre unique chaque instance d'une entité. La plupart des entités n'ont qu'une seule clé qui est alors transformée en *clé primaire* pour les bases de données relationnelles.
*Note: une entité peut avoir d'autres clés, on parle d'__alternate keys__. Elles seront présentées dans les exemples sur les relations entre entités.*
Si on utilise les *conventions d'écriture*, une propriété pour être transformée en clé doit respecter les contraintes suivantes :
* elle doit être nommée ```Id``` ou ```ID```,
* elle doit être nommée ```<typeDeLEntite>Id```, e.g. ```NounoursId```.
Les autres contraintes sur une clé dans le cas de l'utilisation des *conventions d'écriture* sont :
* elle doit être de type ```int```, ```string```, ```byte[]```. Toutefois, certains types nécessitent l'utilisation de converteurs pour être utilisés avec certains fournisseurs. Je conseille donc l'utilisation de ```int``` qui marche avec la grande majorité des fournisseurs.
* elle est générée lors de l'insertion en base.
Les autres modes (*data annotations* et *Fluent API*) offrent plus de solutions quant à la gestion des clés.
## La classe ```Nounours```
La classe ```Nounours``` utilise les conventions d'écriture.
* Par défaut, les propriétés utilisées comme clés primaires sont en mode **Generated on add**.
Une nouvelle valeur est donc générée lors de l'insertion d'une nouvelle entité en base. Les valeurs des autres propriétés ne sont pas générées lors de l'insertion ou de la mise à jour.
* Dans cette classe, j'ai respecté la contrainte de nommage qui propose ```Id``` ou ```ID```
```csharp
public int ID
{
get; set;
}
```
## La classe ```Cylon```
La classe ```Cylon``` utilise les conventions d'écriture.
* Dans cette classe, j'ai respecté la contrainte de nommage qui propose ```<TypeDeLEntité>Id```
```csharp
public int CylonId
{
get; set;
}
```
### La classe ```Program```
Cette classe est le point d'entrée du programme :
* Elle crée des instances de ```Nounours``` et de ```Cylon``` et les ajoute en base après avoir nettoyé les tables au préalables.
* Elle affiche les ```Nounours``` et les ```Cylon```.
*Notez la génération des identifiants.*
## Comment exécuter cet exemple ?
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
* Ouvrez la *Console du Gestionnaire de package*, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
* Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
```
cd .\p08_BDD_EntityFramework\ex_042_004_Keys_conventions
```
*Note*:
si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :
```dotnet tool install --global dotnet-ef```
* Migration :
```
dotnet ef migrations add migration_ex_042_004
```
* Création de la table :
```
dotnet ef database update
```
* Génération et exécution
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_004_Keys_conventions**.
* Comment vérifier le contenu des bases de données SQL Server et SQLite ?
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* Déployez dans l'*Explorateur d'objets SQL Server* :
* *SQL Server*,
* puis *(localdb)\MSSQLLocalDB ...*,
* puis *Bases de données*
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_004_Keys_conventions.Nounours.mdf*
* puis *Tables*
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* Le résultat de l'exécution peut être :
```
database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :
Nounours 1: Chewbacca (27/05/1977, 1234567 poils)
Nounours 2: Yoda (21/05/1980, 3 poils)
Nounours 3: Ewok (25/05/1983, 3456789 poils)
Cylon 1: John Cavil, Number 1
Cylon 2: Leoben Conoy, Number 2
Cylon 3: D'Anna Biers, Number 3
Cylon 4: Simon, Number 4
Cylon 5: Aaron Doral, Number 5
Cylon 6: Caprica 6, Number 6
Cylon 7: Daniel, Number 7
Cylon 8: Boomer, Number 8
Cylon 9: Athena, Number 8
```
*Note: les identifiants peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données.*

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</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"/>
</ItemGroup>
</Project>

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />

Loading…
Cancel
Save