From f2ddc019a755acb4c51fc7f0e99142af8ab41bf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Chevaldonn=C3=A9?= Date: Tue, 7 Jan 2020 22:08:16 +0100 Subject: [PATCH] added ex_042_006 et ex_042_007 --- Exemples.sln | 46 +++ p08_BDD_EntityFramework/ReadMe.md | 7 +- .../ex_041_001_ConnectionStrings/ReadMe.md | 11 +- .../ex_041_004_TestingInMemory/ReadMe.md | 11 +- .../ex_042_001_EF_CF_conventions/Nounours.cs | 45 --- .../ex_042_001_EF_CF_conventions/ReadMe.md | 16 +- .../Nounours.cs | 45 --- .../ReadMe.md | 63 ++-- .../ex_042_003_EF_CF_Fluent_API/Nounours.cs | 45 --- .../ex_042_003_EF_CF_Fluent_API/ReadMe.md | 18 +- .../ex_042_004_Keys_conventions/Nounours.cs | 45 --- .../ex_042_004_Keys_conventions/ReadMe.md | 7 +- .../Nounours.cs | 81 ----- .../NounoursDBEntities.cs | 15 - .../Program.cs | 71 ----- .../ReadMe.md | 175 ----------- .../Nounours.cs | 45 --- .../ReadMe.md | 9 +- .../Nounours.cs | 102 ------- .../NounoursDBEntities.cs | 57 ---- .../Program.cs | 81 ----- .../ReadMe.md | 247 --------------- .../ex_042_006_Keys_FluentAPI/Cylon.cs | 31 ++ .../ex_042_006_Keys_FluentAPI/DBEntities.cs | 36 +++ .../ex_042_006_Keys_FluentAPI/Nounours.cs | 38 +++ .../ex_042_006_Keys_FluentAPI/Ordinateur.cs | 29 ++ .../ex_042_006_Keys_FluentAPI/Program.cs | 100 ++++++ .../ex_042_006_Keys_FluentAPI/ReadMe.md | 195 ++++++++++++ .../ex_042_006_Keys_FluentAPI.csproj} | 2 +- .../Nounours.cs | 93 ------ .../NounoursDBEntities.cs | 78 ----- ...x_042_006_ValueGeneration_FluentAPI.csproj | 12 - .../ex_042_007_ValueGeneration/Nounours.cs | 54 ++++ .../NounoursDBEntities.cs | 44 +++ .../Program.cs | 22 +- .../ex_042_007_ValueGeneration/ReadMe.md | 284 ++++++++++++++++++ .../ex_042_007_ValueGeneration.csproj} | 0 37 files changed, 948 insertions(+), 1312 deletions(-) delete mode 100644 p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Nounours.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/NounoursDBEntities.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Program.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md delete mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Cylon.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/DBEntities.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Nounours.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Ordinateur.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Program.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ReadMe.md rename p08_BDD_EntityFramework/{ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj => ex_042_006_Keys_FluentAPI/ex_042_006_Keys_FluentAPI.csproj} (95%) delete mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs delete mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj create mode 100644 p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Nounours.cs create mode 100644 p08_BDD_EntityFramework/ex_042_007_ValueGeneration/NounoursDBEntities.cs rename p08_BDD_EntityFramework/{ex_042_006_ValueGeneration_FluentAPI => ex_042_007_ValueGeneration}/Program.cs (80%) create mode 100644 p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ReadMe.md rename p08_BDD_EntityFramework/{ex_042_004_ValueGeneration_conventions/ex_042_004_ValueGeneration_conventions.csproj => ex_042_007_ValueGeneration/ex_042_007_ValueGeneration.csproj} (100%) diff --git a/Exemples.sln b/Exemples.sln index fd19b98..7e8742e 100644 --- a/Exemples.sln +++ b/Exemples.sln @@ -568,6 +568,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_Keys_conventions EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_005_Keys_data_annotations", "p08_BDD_EntityFramework\ex_042_005_Keys_data_annotations\ex_042_005_Keys_data_annotations.csproj", "{27725449-27B2-47ED-A2B3-738851E55C64}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_006_Keys_FluentAPI", "p08_BDD_EntityFramework\ex_042_006_Keys_FluentAPI\ex_042_006_Keys_FluentAPI.csproj", "{64EA0021-231A-421F-A616-3973C0106E99}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_007_ValueGeneration", "p08_BDD_EntityFramework\ex_042_007_ValueGeneration\ex_042_007_ValueGeneration.csproj", "{DA7ADAF3-34FF-4B97-8306-EF490A7A713A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -4724,6 +4728,46 @@ Global {27725449-27B2-47ED-A2B3-738851E55C64}.Release|x64.Build.0 = Release|Any CPU {27725449-27B2-47ED-A2B3-738851E55C64}.Release|x86.ActiveCfg = Release|Any CPU {27725449-27B2-47ED-A2B3-738851E55C64}.Release|x86.Build.0 = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM.ActiveCfg = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM.Build.0 = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|ARM64.Build.0 = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|x64.ActiveCfg = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|x64.Build.0 = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|x86.ActiveCfg = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Debug|x86.Build.0 = Debug|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|Any CPU.Build.0 = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM.ActiveCfg = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM.Build.0 = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM64.ActiveCfg = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|ARM64.Build.0 = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|x64.ActiveCfg = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|x64.Build.0 = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|x86.ActiveCfg = Release|Any CPU + {64EA0021-231A-421F-A616-3973C0106E99}.Release|x86.Build.0 = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM.Build.0 = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|ARM64.Build.0 = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x64.ActiveCfg = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x64.Build.0 = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x86.ActiveCfg = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Debug|x86.Build.0 = Debug|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|Any CPU.Build.0 = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM.ActiveCfg = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM.Build.0 = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM64.ActiveCfg = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|ARM64.Build.0 = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x64.ActiveCfg = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x64.Build.0 = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x86.ActiveCfg = Release|Any CPU + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4993,6 +5037,8 @@ Global {BAB08578-898E-48C5-8470-6AC72D49B0D8} = {5B333C02-67B7-4A4C-AA58-2710C183292B} {DBEE3EA3-9B59-4688-B7D7-6A4ABA6E1991} = {5B333C02-67B7-4A4C-AA58-2710C183292B} {27725449-27B2-47ED-A2B3-738851E55C64} = {5B333C02-67B7-4A4C-AA58-2710C183292B} + {64EA0021-231A-421F-A616-3973C0106E99} = {5B333C02-67B7-4A4C-AA58-2710C183292B} + {DA7ADAF3-34FF-4B97-8306-EF490A7A713A} = {5B333C02-67B7-4A4C-AA58-2710C183292B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E} diff --git a/p08_BDD_EntityFramework/ReadMe.md b/p08_BDD_EntityFramework/ReadMe.md index 8a38e6f..7a4e8e5 100644 --- a/p08_BDD_EntityFramework/ReadMe.md +++ b/p08_BDD_EntityFramework/ReadMe.md @@ -27,6 +27,8 @@ Ce chapitre s'attardera sur le lien entre le mod * **ex_042_003 : Fluent API** : explique comment utiliser la *Fluent API* pour personnaliser la transformation d'une entité en table. * **ex_042_004 : Keys with conventions** : explique comment créer les clés primaires d'une entité lorsqu'on utilise les conventions d'écriture. * **ex_042_005 : Keys with data annotations** : explique comment créer les clés primaires d'une entité lorsqu'on utilise les *data annotations*. + * **ex_042_006 : Keys with Fluent API** : explique comment créer les clés primaires d'une entité lorsqu'on utilise la *Fluent API*. + * **ex_042_007 : Value Generation** : explique comment faire générer des valeurs automatiquement lors de l'insertion ou de la mise à jour 3. *Schemas and migrations* : Le but de ce chapitre sera de vous montrer comment garder votre modèle et votre base de données synchronisés. @@ -137,7 +139,10 @@ dotnet ef database update Si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -```dotnet tool install --global dotnet-ef``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + ##### Utilisez votre base de données via Entity Framework Core * Editez *Program.cs* et ajoutez le code suivant : diff --git a/p08_BDD_EntityFramework/ex_041_001_ConnectionStrings/ReadMe.md b/p08_BDD_EntityFramework/ex_041_001_ConnectionStrings/ReadMe.md index 2d8be25..c960f6b 100644 --- a/p08_BDD_EntityFramework/ex_041_001_ConnectionStrings/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_041_001_ConnectionStrings/ReadMe.md @@ -51,7 +51,10 @@ cd .\p08_BDD_EntityFramework\ex_041_001_ConnectionStrings *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -```dotnet tool install --global dotnet-ef``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + ### Migrations *Note :* normalement, la commande pour effectuer une migration est : @@ -72,6 +75,12 @@ dotnet ef database update --context SQLiteContext ### Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple. +Le résultat de l'exécution peut donner : +``` +Creates and inserts new Nounours +Creates and inserts new Nounours +``` + ## Comment vérifier le contenu des bases de données SQL Server et SQLite ? ### SqlServer Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*. diff --git a/p08_BDD_EntityFramework/ex_041_004_TestingInMemory/ReadMe.md b/p08_BDD_EntityFramework/ex_041_004_TestingInMemory/ReadMe.md index 3625d45..1346fa6 100644 --- a/p08_BDD_EntityFramework/ex_041_004_TestingInMemory/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_041_004_TestingInMemory/ReadMe.md @@ -121,7 +121,10 @@ cd .\p08_BDD_EntityFramework\ex_041_004_ConsoleTests_w_SqlServer *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -```dotnet tool install --global dotnet-ef``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : comme la classe dérivant de ```DbContext``` n'est pas dans l'application Console, nous devons préciser dans quel projet elle se trouve en ajoutant ```--project ../ex_041_004_TestingInMemory```. ``` @@ -134,7 +137,11 @@ dotnet ef database update --project ../ex_041_004_TestingInMemory * Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple **ex_041_004_ConsoleTests_w_SqlServer**. - * Comment vérifier le contenu des bases de données SQL Server et SQLite ? + * Le résultat de l'exécution doit ressembler à : + ``` +Creates and inserts new Nounours +``` + * Comment vérifier le contenu des bases de données SQL Server ? 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*. diff --git a/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/Nounours.cs b/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/Nounours.cs index 9554707..0f5be79 100644 --- a/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/Nounours.cs +++ b/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/Nounours.cs @@ -27,51 +27,6 @@ namespace ex_042_001_EF_CF_conventions set; } - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - public override string ToString() { return $"{ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; diff --git a/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/ReadMe.md b/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/ReadMe.md index 420d40e..a1c006b 100644 --- a/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_001_EF_CF_conventions/ReadMe.md @@ -173,7 +173,10 @@ cd .\p08_BDD_EntityFramework\ex_042_001_EF_CF_conventions *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -```dotnet tool install --global dotnet-ef``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : ``` @@ -186,7 +189,16 @@ dotnet ef database update * Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple **ex_042_001_EF_CF_conventions**. - * Comment vérifier le contenu des bases de données SQL Server et SQLite ? + * Le résultat de l'exécution doit ressembler à : + ``` +Base après ajout des 3 nounours et sauvegarde des changements : + 1: Chewbacca (27/05/1977, 1234567 poils) + 2: Yoda (21/05/1980, 3 poils) + 3: Ewok (25/05/1983, 3456789 poils) +``` +_Note : les identifiants peuvent varier en fonction du nombre d'exécutions_ + + * Comment vérifier le contenu des bases de données SQL Server ? 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*. diff --git a/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/Nounours.cs b/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/Nounours.cs index 2605649..75f809b 100644 --- a/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/Nounours.cs +++ b/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/Nounours.cs @@ -37,51 +37,6 @@ namespace ex_042_002_EF_CF_data_annotations set; } - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - public override string ToString() { return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; diff --git a/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/ReadMe.md b/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/ReadMe.md index 6374883..892ba58 100644 --- a/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_002_EF_CF_data_annotations/ReadMe.md @@ -91,37 +91,6 @@ public class Nounours set; } - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - 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); - } - - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - public override string ToString() { return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; @@ -288,9 +257,10 @@ cd .\p08_BDD_EntityFramework\ex_042_002_EF_CF_data_annotations *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -``` -dotnet tool install --global dotnet-ef -``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : ``` @@ -303,7 +273,16 @@ dotnet ef database update * Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple **ex_042_002_EF_CF_data_annotations**. - * Comment vérifier le contenu des bases de données SQL Server et SQLite ? + * Le résultat de l'exécution peut ressembler à : + ``` +Base après ajout des 3 nounours et sauvegarde des changements : + fbc3f7c5-a333-4d07-f7f2-08d7907e5437: Chewbacca (27/05/1977, 1234567 poils) + 63b18e10-a683-4144-f7f3-08d7907e5437: Yoda (21/05/1980, 3 poils) + c4eab29b-315b-416e-f7f4-08d7907e5437: Ewok (25/05/1983, 3456789 poils) +``` +_Note : les identifiants seront bien sûr différents_ + + * Comment vérifier le contenu des bases de données SQL Server ? 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*. @@ -319,12 +298,10 @@ Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'obj * Vous devriez maintenant pouvoir voir les données suivantes dans le tableau : - |UniqueId |Nom|Naissance - |---|---|--- - |fbc3f7c5-a333-4d07-f7f2-08d7907e5437|Chewbacca|27/05/1977 - |63b18e10-a683-4144-f7f3-08d7907e5437|Yoda|21/05/1980 - |c4eab29b-315b-416e-f7f4-08d7907e5437|Ewok|25/05/1983 + |UniqueId |Nom|Naissance|NbPoils|DateDInsertion|DernièreModification + |---|---|---|---|---|--- + |49102173-b82a-4b4d-1d02-08d793b46eb3|Chewbacca|27/05/1977|1234567|07/01/2020 20:59:05|07/01/2020 20:59:08 + |ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3|Yoda|21/05/1980|3|07/01/2020 20:59:05|07/01/2020 20:59:05 + |ccee4eaf-4b50-48ec-1d04-08d793b46eb3||07/01/2020|0|07/01/2020 20:59:05|07/01/2020 20:59:05 -*Notes: les identifiants seront bien sûr différents.* -*Notez l'absence de la colonne "NbPoils"* -*Notez le nom de la colonne "Naissance" et le formatage de la date* \ No newline at end of file +*Note : les identifiants seront bien sûr différents.* diff --git a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Nounours.cs b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Nounours.cs index 8cbf24b..30ae52b 100644 --- a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Nounours.cs +++ b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Nounours.cs @@ -27,51 +27,6 @@ namespace ex_042_003_EF_CF_Fluent_API set; } - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - public override string ToString() { return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; diff --git a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/ReadMe.md b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/ReadMe.md index 3d6ce42..987060d 100644 --- a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/ReadMe.md @@ -256,9 +256,10 @@ cd .\p08_BDD_EntityFramework\ex_042_003_EF_CF_Fluent_API *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -``` -dotnet tool install --global dotnet-ef -``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : ``` @@ -270,8 +271,17 @@ dotnet ef database update ``` * Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple **ex_042_003_EF_CF_Fluent_API**. + + * Le résultat de l'exécution pourra ressembler à : + ``` +Base après ajout des 3 nounours et sauvegarde des changements : + 30817fff-8736-457d-db18-08d7908d7986: Chewbacca (27/05/1977, 1234567 poils) + aa4469c4-a6c8-4077-db19-08d7908d7986: Yoda (21/05/1980, 3 poils) + 69cb5892-6750-4629-db1a-08d7908d7986: Ewok (25/05/1983, 3456789 poils) +``` +*Notes: les identifiants seront bien sûr différents.* - * Comment vérifier le contenu des bases de données SQL Server et SQLite ? + * Comment vérifier le contenu des bases de données SQL Server ? 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*. diff --git a/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/Nounours.cs b/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/Nounours.cs index 5acaab6..40ce89c 100644 --- a/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/Nounours.cs +++ b/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/Nounours.cs @@ -27,51 +27,6 @@ namespace ex_042_004_Keys_conventions set; } - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - 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)"; diff --git a/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/ReadMe.md b/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/ReadMe.md index 8531e00..13613e4 100644 --- a/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_004_Keys_conventions/ReadMe.md @@ -67,7 +67,10 @@ 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``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : ``` @@ -80,7 +83,7 @@ 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 ? + * Comment vérifier le contenu des bases de données SQL Server ? 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*. diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Nounours.cs b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Nounours.cs deleted file mode 100644 index 44d0b03..0000000 --- a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Nounours.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; - -namespace ex_042_004_ValueGeneration_conventions -{ - public class Nounours - { - public int ID - { - get; set; - } - - public string Nom - { - get; - set; - } - - public DateTime DateDeNaissance - { - get; - set; - } - - public int NbPoils - { - get; - set; - } - - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - - public override string ToString() - { - return $"{ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; - } - - } -} diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/NounoursDBEntities.cs b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/NounoursDBEntities.cs deleted file mode 100644 index 51225c8..0000000 --- a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/NounoursDBEntities.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace ex_042_004_ValueGeneration_conventions -{ - class NounoursDBEntities : DbContext - { - public virtual DbSet NounoursSet { get; set; } - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_004_ValueGeneration_conventions.Nounours.mdf;Trusted_Connection=True;"); - } - } -} diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Program.cs b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Program.cs deleted file mode 100644 index 12c7d71..0000000 --- a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/Program.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; -using static System.Console; - -namespace ex_042_004_ValueGeneration_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 (); - - try - { - using (NounoursDBEntities db = new NounoursDBEntities()) - { - //nettoyage de la base de données - if (db.NounoursSet.Count() > 0) - { - foreach (var n in db.NounoursSet) - { - db.NounoursSet.Remove(n); - } - db.SaveChanges(); - } - - //ajout des nounours dans la base de données - db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); - db.SaveChanges(); - - WriteLine("database after cleaning and adding 3 Nounours and saving changes :"); - foreach (var n in db.NounoursSet) - { - WriteLine($"\t{n}"); - } - - //essai d'ajout d'un Nounours existant - try - { - WriteLine("\nTry to insert an existing entity"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); - } - catch (DbUpdateException exc) - { - WriteLine(exc.Message); - } - - WriteLine("\nDisplay the last Nounours with default values"); - Nounours e = db.NounoursSet.ToList().Last(); - string nameStr = e.Nom != null ? e.Nom : "null"; - WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}"); - } - } - 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(); - } - } -} diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md deleted file mode 100644 index 9d1918c..0000000 --- a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md +++ /dev/null @@ -1,175 +0,0 @@ -# ex_042_004_ValueGeneration_conventions -*04/01/2020 ⋅ Marc Chevaldonné* - ---- - -Cet exemple traite de la génération des valeurs de propriétés par la base de données, lors de l'ajout ou de l'insertion. - -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 génération de valeurs 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_ValueGeneration_data_annotations**. -Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : **ex_042_006_ValueGeneration_FluentAPI**. - ---- - -## La génération de valeurs -**Entity Framework Core** propose trois solutions de génération de valeurs : -* **None** : c'est à l'utilisateur de donner une valeur avant l'insertion, sinon, la valeur par défaut du type est utilisée. -Par défaut, c'est le mode utilisé pour toutes les propriétés, sauf les clés primaires. C'est donc uniquement dans ce cas qu'on devra chercher à l'utiliser si on veut gérer soi-même les valeurs des clés. -* **Generated on add** : on demande à la base de générer une valeur lors de l'insertion en base d'un nouvel élément. -Pour savoir si l'élément est nouveau, **EF Core** regarde si l'entité était déjà en base ou non. Si la valeur de la propriété de la clé primaire à générer est la valeur par défaut (par exemple, ```null``` pour ```string```, ```0```pour ```int```, ```Guid.Empty``` pour ```Guid```...), une nouvelle valeur est générée, sinon, rien n'est changé. -Ce mode est souvent utilisé pour les clés primaires ou les dates. -* **Generated on add or update** : on demande à la base de générer une valeur lors de l'insertion ou de la mise à jour de l'élément en base. -Ce mode est souvent utilisé pour les dates représentant des mises à jour. - -## 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. -```csharp -public class Nounours -{ - public int ID - { - get; set; - } - - public string Nom - { - get; - set; - } - - public DateTime DateDeNaissance - { - get; - set; - } - - public int NbPoils - { - get; - set; - } -} -``` - -**En résumé** : -Si on utilise des conventions d'écriture : -* seule la propriété utilisée comme clé primaire est générée lors de l'insertion en table : elle est reconnue si elle a un nom reconnu pour être une clé ("ID" par exemple) ; la génération a lieu si le type peut être utilisé (```int```, ```Guid```, ...). -* toutes les autres propriétés sont en mode **None**, c'est-à-dire que les valeurs ne sont jamais générées pas la base. - -### La classe ```Program``` -Cette classe est le point d'entrée du programme : -* Elle crée des instances de ```Nounours``` -Notez que la dernière ne donne aucune valeur pour les propriétés pour tester les valeurs par défaut. -```csharp -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 (); -``` -* Elle démarre une connexion à la base de données -```csharp -using (NounoursDBEntities db = new NounoursDBEntities()) -{ -//... -} -``` -* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide. -Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base. -Notez la création automatique des ID. -_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._ -```csharp -//nettoyage de la base de données -if (db.NounoursSet.Count() > 0) -{ - foreach (var n in db.NounoursSet) - { - db.NounoursSet.Remove(n); - } - db.SaveChanges(); -} - -//ajout des nounours dans la base de données -db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); -db.SaveChanges(); - -WriteLine("database after cleaning and adding 3 Nounours and saving changes :"); -foreach (var n in db.NounoursSet) -{ - WriteLine($"\t{n}"); -} -``` -* Elle tente d'ajouter une entité dont l'ID n'est pas l'identifiant par défaut (0) à la base. Ceci lance une ```DbUpdateException```. -```csharp -//essai d'ajout d'un Nounours existant -try -{ - WriteLine("\nTry to insert an existing entity"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); -} -catch (DbUpdateException exc) -{ - WriteLine(exc.Message); -} -``` -* Elle affiche les valeurs des propriétés du dernier Nounours pour montrer les valeurs par défaut : ```null``` pour le ```string```, ```0``` pour l'```int```, ```01/01/0001 00:00:00``` pour le ```DateTime```. -```csharp -WriteLine("\nDisplay the last Nounours with default values"); -Nounours e = db.NounoursSet.ToList().Last(); -string nameStr = e.Nom != null ? e.Nom : "null"; -WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}"); -``` - -## 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_ValueGeneration_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_ValueGeneration_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*. - - -* 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_ValueGeneration_conventions.Nounours.mdf* - * puis *Tables* - * Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données* - - - * Vous devriez maintenant pouvoir voir les données suivantes dans le tableau : - - |ID |Nom|DateDeNaissance|NbPoils - |---|---|---|--- - |1|Chewbacca|27/05/1977 00:00:00|1234567 - |2|Yoda|21/05/1980 00:00:00|3 - |3|NULL|01/01/0001 00:00:00|0 - -*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.* \ No newline at end of file diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs index 6db9406..533f763 100644 --- a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs @@ -31,51 +31,6 @@ namespace ex_042_005_Keys_data_annotations set; } - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - public override string ToString() { return $"Nounours {UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md index 1651f1a..b500021 100644 --- a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md @@ -82,7 +82,10 @@ cd .\p08_BDD_EntityFramework\ex_042_005_Keys_data_annotations *Note*: si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : -```dotnet tool install --global dotnet-ef``` +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + * Migration : ``` @@ -95,7 +98,7 @@ dotnet ef database update * Génération et exécution Vous pouvez maintenant générer et exécuter l'exemple **ex_042_005_Keys_data_annotations**. - * Comment vérifier le contenu des bases de données SQL Server et SQLite ? + * Comment vérifier le contenu des bases de données SQL Server ? 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*. @@ -104,7 +107,7 @@ Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'obj * *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 celle portant le nom de votre migration, dans mon cas : *ex_042_005_Keys_data_annotations.Nounours.mdf* * puis *Tables* * Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données* diff --git a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs deleted file mode 100644 index d68360f..0000000 --- a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace ex_042_005_ValueGeneration_data_annotations -{ - [Table("TableNounours")] - public class Nounours - { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid UniqueId - { - get; set; - } - - [Required] - [MaxLength(256)] - public string Nom - { - get; - set; - } - - [Column("Naissance", TypeName = "date")] - public DateTime DateDeNaissance - { - get; - set; - } - - [NotMapped] - public int NbPoils - { - get; - set; - } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public DateTime InsertionDate - { - get; set; - } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public DateTime LastModified - { - get; set; - } - - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - - public override string ToString() - { - return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on:{InsertionDate}, Last modified on: {LastModified})"; - } - - } -} diff --git a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs deleted file mode 100644 index 4fea1de..0000000 --- a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -namespace ex_042_005_ValueGeneration_data_annotations -{ - class NounoursDBEntities : DbContext - { - public virtual DbSet NounoursSet { get; set; } - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_005_ValueGeneration_data_annotations.Nounours.mdf;Trusted_Connection=True;"); - //optionsBuilder.UseSqlite($"Data Source=ex_042_005_ValueGeneration_data_annotations.Nounours.db"); - } - - internal static string DefaultValues = @"ALTER TABLE [dbo].[TableNounours] ADD CONSTRAINT DF_TableNounours DEFAULT GETDATE() FOR InsertionDate -ALTER TABLE [dbo].[TableNounours] ADD CONSTRAINT DF_TableNounours2 DEFAULT GETDATE() FOR LastModified"; - - internal static string InsertionDateTrigger = @"CREATE TRIGGER [dbo].[InsertionDateTrigger] - ON [dbo].[TableNounours] - AFTER INSERT -AS -BEGIN - SET NOCOUNT ON; - - IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN; - - DECLARE @Id uniqueidentifier - - SELECT @Id = INSERTED.UniqueId - FROM INSERTED - - UPDATE dbo.TableNounours - SET InsertionDate = GETDATE(), LastModified = GETDATE() - WHERE UniqueId = @Id -END"; - - internal static string LastModifiedTrigger = @"CREATE TRIGGER [dbo].[LastModifiedTrigger] - ON [dbo].[TableNounours] - AFTER UPDATE -AS -BEGIN - SET NOCOUNT ON; - - IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN; - - DECLARE @Id uniqueidentifier - - SELECT @Id = INSERTED.UniqueId - FROM INSERTED - - UPDATE dbo.TableNounours - SET LastModified = GETDATE() - WHERE UniqueId = @Id -END"; - } -} diff --git a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs deleted file mode 100644 index 723c570..0000000 --- a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; -using static System.Console; - -namespace ex_042_005_ValueGeneration_data_annotations -{ - 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 (); - - try - { - using (NounoursDBEntities db = new NounoursDBEntities()) - { - //nettoyage de la base de données - if (db.NounoursSet.Count() > 0) - { - foreach (var n in db.NounoursSet) - { - db.NounoursSet.Remove(n); - } - db.SaveChanges(); - } - - //ajout des nounours dans la base de données - db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); - db.SaveChanges(); - - WriteLine("database after cleaning and adding 3 Nounours and saving changes :"); - foreach (var n in db.NounoursSet) - { - WriteLine($"\t{n}"); - } - - //essai d'ajout d'un Nounours existant - try - { - WriteLine("\nTry to insert an existing entity"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); - } - catch (DbUpdateException exc) - { - WriteLine(exc.Message); - } - - //modification d'un Nounours existant - WriteLine("\nModification of the name of Chewbacca to Chewie"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); - foreach (var n in db.NounoursSet) - { - WriteLine($"\t{n}"); - } - - WriteLine("\nDisplay the last Nounours with default values"); - Nounours e = db.NounoursSet.ToList().Last(); - string nameStr = e.Nom != null ? e.Nom : "null"; - WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}"); - } - } - 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(); - } - } -} diff --git a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md deleted file mode 100644 index d62d872..0000000 --- a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md +++ /dev/null @@ -1,247 +0,0 @@ -# ex_042_005_ValueGeneration_data_annotations -*05/01/2020 ⋅ Marc Chevaldonné* - ---- - -Cet exemple traite de la génération des valeurs de propriétés par la base de données, lors de l'ajout ou de l'insertion. - -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 génération de valeurs lors de l'utilisation des **data annotations**. -Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : **ex_042_004_ValueGeneration_conventions**. -Vous pourrez trouver une version plus ou moins équivalente avec la *Fluent API* ici : **ex_042_006_ValueGeneration_FluentAPI**. - ---- - -## La génération de valeurs -**Entity Framework Core** propose trois solutions de génération de valeurs : -* **None** : c'est à l'utilisateur de donner une valeur avant l'insertion, sinon, la valeur par défaut du type est utilisée. -Par défaut, c'est le mode utilisé pour toutes les propriétés, sauf les clés primaires. C'est donc uniquement dans ce cas qu'on devra chercher à l'utiliser si on veut gérer soi-même les valeurs des clés. -* **Generated on add** : on demande à la base de générer une valeur lors de l'insertion en base d'un nouvel élément. -Pour savoir si l'élément est nouveau, **EF Core** regarde si l'entité était déjà en base ou non. Si la valeur de la propriété de la clé primaire à générer est la valeur par défaut (par exemple, ```null``` pour ```string```, ```0```pour ```int```, ```Guid.Empty``` pour ```Guid```...), une nouvelle valeur est générée, sinon, rien n'est changé. -Ce mode est souvent utilisé pour les clés primaires ou les dates. -* **Generated on add or update** : on demande à la base de générer une valeur lors de l'insertion ou de la mise à jour de l'élément en base. -Ce mode est souvent utilisé pour les dates représentant des mises à jour. - -## La classe ```Nounours``` -La classe ```Nounours``` utilise les *data annotations*. -```csharp -[Table("TableNounours")] -public class Nounours -{ - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public Guid UniqueId - { - get; set; - } - - [Required] - [MaxLength(256)] - public string Nom - { - get; - set; - } - - [Column("Naissance", TypeName = "date")] - public DateTime DateDeNaissance - { - get; - set; - } - - [NotMapped] - public int NbPoils - { - get; - set; - } - - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public DateTime InsertionDate - { - get; set; - } - - [DatabaseGenerated(DatabaseGeneratedOption.Computed)] - public DateTime LastModified - { - get; set; - } - - ///... -} -``` - -* 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 si on ne précise rien. -* Dans la classe ```Nounours```, on peut donc voir que les propriétés : ```Nom``` et ```DateDeNaissance``` n'ont pas de valeurs pas générées lors de l'insertion ou de la mise à jour (mode par défaut). (```NbPoils``` n'est pas *mappée*). -* ```csharp -[Required] -[MaxLength(256)] -public string Nom -{ - get; - set; -} - -[Column("Naissance", TypeName = "date")] -public DateTime DateDeNaissance -{ - get; - set; -} - -[NotMapped] -public int NbPoils -{ - get; - set; -} -``` -* La propriété ```UniqueId``` est remise à jour lors de l'insertion en base (grâce à ```DatabaseGenerated(DatabaseGeneratedOption.Identity)```). -```csharp -[Key] -[DatabaseGenerated(DatabaseGeneratedOption.Identity)] -public Guid UniqueId -{ - get; set; -} -``` -* La propriété ```InsertionDate``` est remise à jour lors de l'insertion en base (grâce à ```DatabaseGenerated(DatabaseGeneratedOption.Identity)```). -```csharp -[DatabaseGenerated(DatabaseGeneratedOption.Identity)] -public DateTime InsertionDate -{ - get; set; -} -``` -* La propriété ```LastModified``` est remise à jour lors de l'insertion ou de la mise à jour d'une propriété en base (grâce à ```DatabaseGenerated(DatabaseGeneratedOption.Computed)```). -```csharp -[DatabaseGenerated(DatabaseGeneratedOption.Computed)] -public DateTime LastModified -{ - get; set; -} -``` - -**En résumé** : -Si on utilise des conventions d'écriture : -* seule la propriété utilisée comme clé primaire est générée lors de l'insertion en table : elle est reconnue si elle a un nom reconnu pour être une clé ("ID" par exemple) ; la génération a lieu si le type peut être utilisé (```int```, ```Guid```, ...). -* toutes les autres propriétés sont en mode **None**, c'est-à-dire que les valeurs ne sont jamais générées pas la base. - -### La classe ```Program``` -Cette classe est le point d'entrée du programme : -* Elle crée des instances de ```Nounours``` -Notez que la dernière ne donne aucune valeur pour les propriétés pour tester les valeurs par défaut. -```csharp -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 (); -``` -* Elle démarre une connexion à la base de données -```csharp -using (NounoursDBEntities db = new NounoursDBEntities()) -{ -//... -} -``` -* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide. -Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base. -Notez la création automatique des ID. -_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._ -```csharp -//nettoyage de la base de données -if (db.NounoursSet.Count() > 0) -{ - foreach (var n in db.NounoursSet) - { - db.NounoursSet.Remove(n); - } - db.SaveChanges(); -} - -//ajout des nounours dans la base de données -db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); -db.SaveChanges(); - -WriteLine("database after cleaning and adding 3 Nounours and saving changes :"); -foreach (var n in db.NounoursSet) -{ - WriteLine($"\t{n}"); -} -``` -* Elle tente d'ajouter une entité dont l'ID n'est pas l'identifiant par défaut (0) à la base. Ceci lance une ```DbUpdateException```. -```csharp -//essai d'ajout d'un Nounours existant -try -{ - WriteLine("\nTry to insert an existing entity"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); -} -catch (DbUpdateException exc) -{ - WriteLine(exc.Message); -} -``` -* Elle affiche les valeurs des propriétés du dernier Nounours pour montrer les valeurs par défaut : ```null``` pour le ```string```, ```0``` pour l'```int```, ```01/01/0001 00:00:00``` pour le ```DateTime```. -```csharp -WriteLine("\nDisplay the last Nounours with default values"); -Nounours e = db.NounoursSet.ToList().Last(); -string nameStr = e.Nom != null ? e.Nom : "null"; -WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}"); -``` - -## 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_ValueGeneration_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_ValueGeneration_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*. - - -* 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_ValueGeneration_conventions.Nounours.mdf* - * puis *Tables* - * Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données* - - - * Vous devriez maintenant pouvoir voir les données suivantes dans le tableau : - - |ID |Nom|DateDeNaissance|NbPoils - |---|---|---|--- - |1|Chewbacca|27/05/1977 00:00:00|1234567 - |2|Yoda|21/05/1980 00:00:00|3 - |3|NULL|01/01/0001 00:00:00|0 - -*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.* \ No newline at end of file diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Cylon.cs b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Cylon.cs new file mode 100644 index 0000000..045bfeb --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Cylon.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace ex_042_006_Keys_FluentAPI +{ + public class Cylon + { + public int FrakId + { + get; set; + } + + public string Name + { + get; set; + } + + public int Generation + { + get; set; + } + + public override string ToString() + { + return $"Cylon {FrakId}: {Name}, Number {Generation}"; + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/DBEntities.cs b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/DBEntities.cs new file mode 100644 index 0000000..5e7fd39 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/DBEntities.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore; + +namespace ex_042_006_Keys_FluentAPI +{ + class DBEntities : DbContext + { + public virtual DbSet NounoursSet { get; set; } + public virtual DbSet CylonsSet { get; set; } + public virtual DbSet Ordinateurs { get; set; } + + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_Keys_FluentAPI.Nounours.mdf;Trusted_Connection=True;"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + //définition de la clé primaire de Nounours + modelBuilder.Entity().HasKey(n => n.UniqueId); + //définition du mode de génération de la clé : génération à l'insertion + modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); + + //définition de la clé primaire de Cylon + modelBuilder.Entity().HasKey(c => c.FrakId); + //définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base) + modelBuilder.Entity().Property(c => c.FrakId).ValueGeneratedNever(); + + //définition d'une clé primaire composite pour Ordinateur + modelBuilder.Entity().HasKey(o => new { o.CodeId, o.Modele }); + //une clé composite ne peut pas être générée par la base + + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Nounours.cs b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Nounours.cs new file mode 100644 index 0000000..e608a6c --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Nounours.cs @@ -0,0 +1,38 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_006_Keys_FluentAPI +{ + public class Nounours + { + public int UniqueId + { + get; set; + } + + public string Nom + { + get; + set; + } + + public DateTime DateDeNaissance + { + get; + set; + } + + public int NbPoils + { + get; + set; + } + + public override string ToString() + { + return $"Nounours {UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; + } + + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Ordinateur.cs b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Ordinateur.cs new file mode 100644 index 0000000..5055bed --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Ordinateur.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ex_042_006_Keys_FluentAPI +{ + class Ordinateur + { + public string Modele + { + get; set; + } + + public string CodeId + { + get; set; + } + + public int Année + { + get; set; + } + + public override string ToString() + { + return $"Computer {CodeId} ({Modele}, {Année})"; + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Program.cs b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Program.cs new file mode 100644 index 0000000..0b5cd0d --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/Program.cs @@ -0,0 +1,100 @@ +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using static System.Console; + +namespace ex_042_006_Keys_FluentAPI +{ + 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 { FrakId = 2, Name = "John Cavil", Generation = 1 }; + Cylon c2 = new Cylon { FrakId = 4, Name = "Leoben Conoy", Generation = 2 }; + Cylon c3 = new Cylon { FrakId = 6, Name = "D'Anna Biers", Generation = 3 }; + Cylon c4 = new Cylon { FrakId = 8, Name = "Simon", Generation = 4 }; + Cylon c5 = new Cylon { FrakId = 10, Name = "Aaron Doral", Generation = 5 }; + Cylon c6 = new Cylon { FrakId = 12, Name = "Caprica 6", Generation = 6 }; + Cylon c7 = new Cylon { FrakId = 14, Name = "Daniel", Generation = 7 }; + Cylon c8 = new Cylon { FrakId = 16, Name = "Boomer", Generation = 8 }; + Cylon c9 = new Cylon { FrakId = 17, Name = "Athena", Generation = 8 }; + + Ordinateur o1 = new Ordinateur { Année = 2019, Modele = "MacBook Pro", CodeId = "IUT_1" }; + Ordinateur o2 = new Ordinateur { Année = 2017, Modele = "MacBook Pro", CodeId = "IUT_2" }; + Ordinateur o3 = new Ordinateur { Année = 2016, Modele = "MacBook Pro", CodeId = "IUT_4" }; + Ordinateur o4 = new Ordinateur { Année = 2019, Modele = "Dell Latitude", CodeId = "IUT_1" }; + Ordinateur o5 = new Ordinateur { Année = 2012, Modele = "Dell Latitude", CodeId = "IUT_2" }; + Ordinateur o6 = new Ordinateur { Année = 2013, Modele = "Dell Latitude", CodeId = "IUT_3" }; + + 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(); + } + + if (db.Ordinateurs.Count() > 0) + { + foreach (var o in db.Ordinateurs) + { + db.Ordinateurs.Remove(o); + } + 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.Ordinateurs.AddRange(new Ordinateur[] { o1, o2, o3, o4, o5, o6 }); + 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}"); + } + foreach (var o in db.Ordinateurs) + { + WriteLine($"\t{o}"); + } + } + } + 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(); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ReadMe.md b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ReadMe.md new file mode 100644 index 0000000..9b71a80 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ReadMe.md @@ -0,0 +1,195 @@ +# ex_042_006_Keys_FluentAPI +*07/01/2020 ⋅ 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 la **Fluent API**. +Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : **ex_042_004_Keys_conventions**. +Vous pourrez trouver une version plus ou moins équivalente avec la *data annotations* ici : **ex_042_005_Keys_data_annotations**. + +--- + +## 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 la *Fluent API*, une propriété pour être transformée en clé doit respecter les contraintes suivantes +(toutes les modifications se font dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```) : +* aucune contrainte sur le nommage de la propriété ; j'ai par exemple choisi ```UniqueId``` pour ```Nounours```, et ```FrakId``` pour ```Cylon```. +* on définit qu'une propriété est une clé primaire avec la méthode d'extension ```HasKey``` sur la classe ```Entity```. +```csharp +//définition de la clé primaire de Nounours +modelBuilder.Entity().HasKey(n => n.UniqueId); +``` +```csharp +//définition de la clé primaire de Cylon +modelBuilder.Entity().HasKey(c => c.FrakId); +``` +* elle peut être générée par la base et dans ce cas, on ajoute à la ```Property``` la méthode d'extension ```ValueGeneratedOnAdd()```. +```csharp +//définition du mode de génération de la clé : génération à l'insertion +modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); +``` +* elle peut ne pas être générée par la base et être gérée par l'utilisateur, dans ce cas on utilise la méthode d'extension ```ValueGeneratedNever()```. +Dans ce dernier cas, c'est à l'utilisateur de gérer ses propres clés et leur unicité dans la base. +```csharp +//définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base) +modelBuilder.Entity().Property(c => c.FrakId).ValueGeneratedNever(); +``` +* elle peut être de différents types ```int```, ```string```, ```Guid```, ```byte[]```... attention toutefois si vous choisissez de laisser la table générer les valeurs car certains fournisseurs ne savent pas générer tous les types. + +## Les clés primaires composites +En *Fluent API* (et seulement en *Fluent API*), on peut définir des clés composites, c'est-à-dire se basant sur plusieurs propriétés. +Pour cela, on utilise également ```HasKey```et on l'associe à un type anonyme, comme par exemple pour la classe ```Ordinateur``` dans cet exemple, dont la clé est un composite des propriétés ```CodeId``` et ```Modele```. +Ainsi, dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```, on ajoute : +```csharp +//définition d'une clé primaire composite pour Ordinateur +modelBuilder.Entity().HasKey(o => new { o.CodeId, o.Modele }); +``` +Les clés composites ne peuvent pas être générées par la base. + +## Les classes POCO ```Nounours```, ```Cylon``` et ```Ordinateur``` +Lorsqu'on utilise la *Fluent API*, ces classes n'ont aucune annotation. La classe ```Ordinateur``` n'a pas de propriété "Id". + +## La classe ```DBEntites : DbContext``` +La classe dérivant de ```DbContext``` ressemble dès lors dans notre exemple à : +```csharp +using Microsoft.EntityFrameworkCore; + +namespace ex_042_006_Keys_FluentAPI +{ + class DBEntities : DbContext + { + public virtual DbSet NounoursSet { get; set; } + public virtual DbSet CylonsSet { get; set; } + public virtual DbSet Ordinateurs { get; set; } + + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_Keys_FluentAPI.Nounours.mdf;Trusted_Connection=True;"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + //définition de la clé primaire de Nounours + modelBuilder.Entity().HasKey(n => n.UniqueId); + //définition du mode de génération de la clé : génération à l'insertion + modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); + + //définition de la clé primaire de Cylon + modelBuilder.Entity().HasKey(c => c.FrakId); + //définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base) + modelBuilder.Entity().Property(c => c.FrakId).ValueGeneratedNever(); + + //définition d'une clé primaire composite pour Ordinateur + modelBuilder.Entity().HasKey(o => new { o.CodeId, o.Modele }); + //une clé composite ne peut pas être générée par la base + + base.OnModelCreating(modelBuilder); + } + } +} +``` + +## La classe ```Program``` +Cette classe est le point d'entrée du programme : +* Elle crée des instances de ```Nounours```, de ```Cylon``` et d'```Ordinateur``` et les ajoute en base après avoir nettoyé les tables au préalables. +Notez que l'utilisateur n'a pas besoin de donner une valeur à ```Nounours.UniqueId``` puisque la base s'en charge, alors qu'il doit donner une valeur à ```Cylon.FrakId``` car la base de ne génère pas les clés. +Si vous ne donnez pas une valeur à ```Cylon.FrakId```, alors la valeur par défaut est donnée (```0```). Il n'y aura pas de problème si cet identifiant n'a pas été donné, mais dès le deuxième ```Cylon```, vous aurez une exception. +_Note : la valeur par défaut pour ```int``` est ```0``` ; pour ```Guid```, ```Guid.Empty``` ; pour ```string```, ```null```..._ +Notez que l'utilisateur doit garantir pour ses instances d'```Ordinateur``` l'unicité des couples ```CodeId```/```Modele```, puisqu'il s'agit d'une clé composite. +```csharp +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 { FrakId = 2, Name = "John Cavil", Generation = 1 }; +Cylon c2 = new Cylon { FrakId = 4, Name = "Leoben Conoy", Generation = 2 }; +Cylon c3 = new Cylon { FrakId = 6, Name = "D'Anna Biers", Generation = 3 }; +Cylon c4 = new Cylon { FrakId = 8, Name = "Simon", Generation = 4 }; +Cylon c5 = new Cylon { FrakId = 10, Name = "Aaron Doral", Generation = 5 }; +Cylon c6 = new Cylon { FrakId = 12, Name = "Caprica 6", Generation = 6 }; +Cylon c7 = new Cylon { FrakId = 14, Name = "Daniel", Generation = 7 }; +Cylon c8 = new Cylon { FrakId = 16, Name = "Boomer", Generation = 8 }; +Cylon c9 = new Cylon { FrakId = 17, Name = "Athena", Generation = 8 }; + +Ordinateur o1 = new Ordinateur { Année = 2019, Modele = "MacBook Pro", CodeId = "IUT_1" }; +Ordinateur o2 = new Ordinateur { Année = 2017, Modele = "MacBook Pro", CodeId = "IUT_2" }; +Ordinateur o3 = new Ordinateur { Année = 2016, Modele = "MacBook Pro", CodeId = "IUT_4" }; +Ordinateur o4 = new Ordinateur { Année = 2019, Modele = "Dell Latitude", CodeId = "IUT_1" }; +Ordinateur o5 = new Ordinateur { Année = 2012, Modele = "Dell Latitude", CodeId = "IUT_2" }; +Ordinateur o6 = new Ordinateur { Année = 2013, Modele = "Dell Latitude", CodeId = "IUT_3" }; +``` +* Elle affiche les ```Nounours```, les ```Cylon``` et les ```Ordinateur```. +*Notez la génération des identifiants pour la classe ```Nounours``` uniquement : si vous exécutez plusieurs fois l'exemple, les clés des ```Nounours``` changent mais pas celles des ```Cylon```.* + +## 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_006_Keys_FluentAPI +``` + *Note*: + si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : + +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + + + * Migration : +``` +dotnet ef migrations add migration ex_042_006 +``` + * 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_006_Keys_FluentAPI**. + + * Comment vérifier le contenu des bases de données SQL Server ? +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*. + + +* 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_006_Keys_FluentAPI.Nounours.mdf* + * puis *Tables* + * Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données* + + +* 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: Ewok (25/05/1983, 3456789 poils) + Nounours 3: Yoda (21/05/1980, 3 poils) + Cylon 2: John Cavil, Number 1 + Cylon 4: Leoben Conoy, Number 2 + Cylon 6: D'Anna Biers, Number 3 + Cylon 8: Simon, Number 4 + Cylon 10: Aaron Doral, Number 5 + Cylon 12: Caprica 6, Number 6 + Cylon 14: Daniel, Number 7 + Cylon 16: Boomer, Number 8 + Cylon 17: Athena, Number 8 + Computer IUT_1 (Dell Latitude, 2019) + Computer IUT_1 (MacBook Pro, 2019) + Computer IUT_2 (Dell Latitude, 2012) + Computer IUT_2 (MacBook Pro, 2017) + Computer IUT_3 (Dell Latitude, 2013) + Computer IUT_4 (MacBook Pro, 2016) +``` +*Note: les identifiants des ```Nounours``` peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données, mais pas ceux des ```Cylon``` puisqu'ils sont gérés par l'utilisateur.* \ No newline at end of file diff --git a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ex_042_006_Keys_FluentAPI.csproj similarity index 95% rename from p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj rename to p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ex_042_006_Keys_FluentAPI.csproj index 8fe0f0a..7b1d557 100644 --- a/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj +++ b/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI/ex_042_006_Keys_FluentAPI.csproj @@ -7,6 +7,6 @@ - + diff --git a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs deleted file mode 100644 index 655c1e8..0000000 --- a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace ex_042_006_ValueGeneration_FluentAPI -{ - public class Nounours - { - public Guid UniqueId - { - get; set; - } - - public string Nom - { - get; - set; - } - - public DateTime DateDeNaissance - { - get; - set; - } - - public int NbPoils - { - get; - set; - } - - public DateTime InsertionDate - { - get; set; - } - - public DateTime LastModified - { - get; set; - } - - /// - /// returns a hash code in order to use this class in hash table - /// - /// hash code - public override int GetHashCode() - { - return Nom.GetHashCode(); - } - - /// - /// checks if the "right" object is equal to this Nounours or not - /// - /// the other object to be compared with this Nounours - /// true if equals, false if not - 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); - } - - /// - /// checks if this Nounours is equal to the other Nounours - /// - /// the other Nounours to be compared with - /// true if equals - public bool Equals(Nounours other) - { - return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance); - } - - public override string ToString() - { - return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on: {InsertionDate}, Last modified on: {LastModified})"; - } - - } -} diff --git a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs deleted file mode 100644 index f2355db..0000000 --- a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs +++ /dev/null @@ -1,78 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; - -namespace ex_042_006_ValueGeneration_FluentAPI -{ - class NounoursDBEntities : DbContext - { - public virtual DbSet NounoursSet { get; set; } - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_ValueGeneration_FluentAPI.Nounours.mdf;Trusted_Connection=True;"); - //optionsBuilder.UseSqlite($"Data Source=ex_042_005_ValueGeneration_data_annotations.Nounours.db"); - } - - 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().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().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().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().Property(n => n.Nom).IsRequired() - .HasMaxLength(256) - .HasDefaultValue("John Doe"); - - //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().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date"); - - //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().Ignore(n => n.NbPoils); - - modelBuilder.Entity().Property(n => n.InsertionDate)/*.ValueGeneratedOnAdd()*/.HasDefaultValue(DateTime.UtcNow); - modelBuilder.Entity().Property(n => n.LastModified)/*.ValueGeneratedOnAddOrUpdate()*/.HasDefaultValue(DateTime.UtcNow); - - //if (this.Database.IsSqlServer()) - //{ - // modelBuilder.Entity().Property(n => n.InsertionDate).HasDefaultValueSql("getdate()"); - // modelBuilder.Entity().Property(n => n.LastModified).HasDefaultValueSql("getdate()"); - - //} - - base.OnModelCreating(modelBuilder); - } - - public override int SaveChanges() - { - ChangeTracker.DetectChanges(); - - foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Added)) - { - item.Property(n => n.InsertionDate).CurrentValue = DateTime.UtcNow; - item.Property(n => n.LastModified).CurrentValue = DateTime.UtcNow; - } - - foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) - { - item.Property(n => n.LastModified).CurrentValue = DateTime.UtcNow; - } - return base.SaveChanges(); - } - } -} diff --git a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj deleted file mode 100644 index 7b54839..0000000 --- a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - netcoreapp3.0 - - - - - - - diff --git a/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Nounours.cs b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Nounours.cs new file mode 100644 index 0000000..487ac73 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Nounours.cs @@ -0,0 +1,54 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_007_ValueGeneration +{ + [Table("TableNounours")] + public class Nounours + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid UniqueId + { + get; set; + } + + [Required] + [MaxLength(256)] + public string Nom + { + get; + set; + } + + [Column("Naissance", TypeName = "date")] + public DateTime DateDeNaissance + { + get; + set; + } + + public int NbPoils + { + get; + set; + } + + public DateTime DateDInsertion + { + get; set; + } + + public DateTime DernièreModification + { + get; set; + } + + public override string ToString() + { + return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on: {DateDInsertion}, Last modified on: {DernièreModification})"; + } + + } +} diff --git a/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/NounoursDBEntities.cs b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/NounoursDBEntities.cs new file mode 100644 index 0000000..6990054 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/NounoursDBEntities.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ex_042_007_ValueGeneration +{ + class NounoursDBEntities : DbContext + { + public virtual DbSet NounoursSet { get; set; } + + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_007_ValueGeneration.Nounours.mdf;Trusted_Connection=True;"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().Property(n => n.Nom).HasDefaultValue(""); + modelBuilder.Entity().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow); + modelBuilder.Entity().Property(n => n.DateDInsertion).HasDefaultValue(DateTime.UtcNow); + modelBuilder.Entity().Property(n => n.DernièreModification).HasDefaultValue(DateTime.UtcNow); + + base.OnModelCreating(modelBuilder); + } + + public override int SaveChanges() + { + ChangeTracker.DetectChanges(); + + foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Added)) + { + item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow; + item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; + } + + foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + { + item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; + } + return base.SaveChanges(); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Program.cs similarity index 80% rename from p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs rename to p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Program.cs index c75f7d9..f2a1700 100644 --- a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs +++ b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/Program.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using static System.Console; -namespace ex_042_006_ValueGeneration_FluentAPI +namespace ex_042_007_ValueGeneration { class Program { @@ -39,19 +39,6 @@ namespace ex_042_006_ValueGeneration_FluentAPI { WriteLine($"\t{n}"); } - - //essai d'ajout d'un Nounours existant - try - { - WriteLine("\nTry to insert an existing entity"); - chewie.Nom = "Chewie"; - db.NounoursSet.Add(chewie); - db.SaveChanges(); - } - catch (DbUpdateException exc) - { - WriteLine(exc.Message); - } } WriteLine("Waits 3 seconds..."); @@ -64,8 +51,11 @@ namespace ex_042_006_ValueGeneration_FluentAPI WriteLine("\nModification of the name of Chewbacca to Chewie"); chewie = db.NounoursSet.First(); chewie.Nom = "Chewie"; - //db.NounoursSet.Add(chewie); db.SaveChanges(); + } + + using (NounoursDBEntities db = new NounoursDBEntities()) + { foreach (var n in db.NounoursSet) { WriteLine($"\t{n}"); @@ -74,7 +64,7 @@ namespace ex_042_006_ValueGeneration_FluentAPI WriteLine("\nDisplay the last Nounours with default values"); Nounours e = db.NounoursSet.ToList().Last(); string nameStr = e.Nom != null ? e.Nom : "null"; - WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}; Insertion date: {e.InsertionDate}; LastModified: {e.LastModified}") ; + WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}; Insertion date: {e.DateDInsertion}; LastModified: {e.DernièreModification}") ; } } catch (SqlException) diff --git a/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ReadMe.md b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ReadMe.md new file mode 100644 index 0000000..a48310d --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ReadMe.md @@ -0,0 +1,284 @@ +# ex_042_007_ValueGeneration +*07/01/2020 ⋅ Marc Chevaldonné* + +--- + +Cet exemple traite de la génération des valeurs de propriétés par la base de données, lors de l'ajout ou de l'insertion. + +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* +* il est conseillé d'avoir également lu les exemples sur les clés primaires : *ex_042_004_Keys_conventions*, *ex_042_005_Keys_data_annotations* et *ex_042_006_Keys_FluentAPI*. + + + +--- + +## La génération de valeurs +**Entity Framework Core** propose trois solutions de génération de valeurs : +* **None** : c'est à l'utilisateur de donner une valeur avant l'insertion, sinon, la valeur par défaut du type est utilisée. +Par défaut, c'est le mode utilisé pour toutes les propriétés, sauf les clés primaires. C'est donc uniquement dans ce cas qu'on devra chercher à l'utiliser si on veut gérer soi-même les valeurs des clés. +* **Generated on add** : on demande à la base de générer une valeur lors de l'insertion en base d'un nouvel élément. +Pour savoir si l'élément est nouveau, **EF Core** regarde si l'entité était déjà en base ou non. Si la valeur de la propriété de la clé primaire à générer est la valeur par défaut (par exemple, ```null``` pour ```string```, ```0```pour ```int```, ```Guid.Empty``` pour ```Guid```...), une nouvelle valeur est générée, sinon, rien n'est changé. +Ce mode est souvent utilisé pour les clés primaires ou les dates. +* **Generated on add or update** : on demande à la base de générer une valeur lors de l'insertion ou de la mise à jour de l'élément en base. +Ce mode est souvent utilisé pour les dates représentant des mises à jour. + +Néanmoins, je trouve que l'utilisation sur des propriétés autres que les clés primaires ou avec des types différents de ```int``` ou ```Guid``` n'est pas simple et varie beaucoup en fonction des fournisseurs. +De plus, je pense que la génération de valeurs n'est généralement utilisée que pour des clés primaires, des dates (date de création, date de dernière modification) ou des *timestamps* (mais dans ce cas, d'autres mécanismes sont prévus). +C'est la raison pour laquelle je pense qu'il est plus simple de se contenter de la réécriture de la méthode ```SaveChanges``` de votre contexte dérivant de ```DbContext```. +Cet exemple propose cette méthode à travers l'ajout d'une date d'insertion et d'une date de dernière modification. + +## La classe ```Nounours``` +La classe ```Nounours``` utilise dans cet exemple les *data annotations*. +Notez l'ajout des propriétés ```DateDInsertion``` et ```DernièreModification```, sans annotations. +```csharp +[Table("TableNounours")] +public class Nounours +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid UniqueId + { + get; set; + } + + [Required] + [MaxLength(256)] + public string Nom + { + get; + set; + } + + [Column("Naissance", TypeName = "date")] + public DateTime DateDeNaissance + { + get; + set; + } + + public int NbPoils + { + get; + set; + } + + public DateTime DateDInsertion + { + get; set; + } + + public DateTime DernièreModification + { + get; set; + } +} +``` + +## La classe NounoursDBEntites + +### Gestion des valeurs par défaut pour les dates. +Dans la méthode ```OnModelCreating``` de ```NounoursDBEntites```, on ajoute deux lignes permettant de donner une valeur par défaut aux deux dates au cas où l'utilisateur de les rentrerait pas : +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) +{ + modelBuilder.Entity().Property(n => n.Nom).HasDefaultValue(""); + modelBuilder.Entity().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow); + modelBuilder.Entity().Property(n => n.DateDInsertion).HasDefaultValue(DateTime.UtcNow); + modelBuilder.Entity().Property(n => n.DernièreModification).HasDefaultValue(DateTime.UtcNow); + + base.OnModelCreating(modelBuilder); +} +``` +Remarque : ceci n'est pas obligatoire pour la génération de valeurs, c'est juste cadeau en passant. Le paragraphe suivant écrase ceci, sauf pour la ```DateDeNaissance```. + +### Génération des valeurs par la base +Dans cette méthode, je décide de réécrire la méthode virtuelle ```SaveChanges``` de ```NounoursDBEntities```. +```csharp +public override int SaveChanges() +{ + ChangeTracker.DetectChanges(); + + foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Added)) + { + item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow; + item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; + } + + foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + { + item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; + } + return base.SaveChanges(); +} +``` + * J'utilise dans cette méthode la propriété ```ChangeTracker``` de ```DbContext``` qui permet de garder la trace des modifications qui sont effectuées localement sur les entités avant la sauvegarde (update, delete, create...). + L'appel de la méthode ```DetectChanges()``` permet de garder la trace de toutes les modifications réalisées sur les instances. + * ```ChangeTracker``` me donne ainsi l'accès aux états des entités, de type ```EntityState```. + * La première requête LINQ me donne accès aux entités de type ```Nounours``` ayant été ajoutées sans être encore sauvegardées. +```csharp +ChangeTracker.Entries().Where(e => e.State == EntityState.Added) +``` +Je récupère ainsi ces entités et modifie la valeur de leurs propriétés ```DateDInsertion``` et ```DernièreModification``` à maintenant (```DateTime.UtcNow```). +```csharp +item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow; +item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; +``` +* Je fais ensuite la même chose en récupérant les entités ayant été mises à jour, c'est-à-dire dont la propriété ```State``` vaut ```EntityState.Modified```, et je modifie leur propriété ```DernièreModification```. +```csharp +foreach (var item in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) +{ + item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow; +} +``` +* Enfin, j'appelle la méthode ```SaveChanges``` de la classe mère. + +En résumé, les valeurs ne sont pas générées sur la base, mais... on s'en fiche non ? + +## La classe ```Program``` +Cette classe est le point d'entrée du programme : +* Elle crée des instances de ```Nounours``` +Notez que la dernière ne donne aucune valeur pour les propriétés pour tester les valeurs par défaut. +```csharp +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 (); +``` +* Elle démarre une connexion à la base de données +```csharp +using (NounoursDBEntities db = new NounoursDBEntities()) +{ +//... +} +``` +* Elle vérifie si la table est vide, et si ce n'est pas le cas, elle la vide. +Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base. +Notez la création automatique des ID. +_Cette partie de l'exemple ne s'exécutera que si la base existe déjà, par exemple lors d'une deuxième exécution._ +```csharp +//nettoyage de la base de données +if (db.NounoursSet.Count() > 0) +{ + foreach (var n in db.NounoursSet) + { + db.NounoursSet.Remove(n); + } + db.SaveChanges(); +} + +//ajout des nounours dans la base de données +db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); +db.SaveChanges(); + +WriteLine("database after cleaning and adding 3 Nounours and saving changes :"); +foreach (var n in db.NounoursSet) +{ + WriteLine($"\t{n}"); +} +``` +* Elle attend ensuite 3 secondes (pour qu'on puisse voir la différence dans les dates entre la mise à jour et l'ajout), puis modifie le nom du premier ```Nounours``` +```csharp +WriteLine("Waits 3 seconds..."); +System.Threading.Thread.Sleep(3000); + +using (NounoursDBEntities db = new NounoursDBEntities()) +{ + //modification d'un Nounours existant + WriteLine("\nModification of the name of Chewbacca to Chewie"); + chewie = db.NounoursSet.First(); + chewie.Nom = "Chewie"; + db.SaveChanges(); +} +``` +* Enfin, elle affiche les ```Nounours```de la base une nouvelle fois, et les valeurs des propriétés du dernier Nounours pour montrer les valeurs par défaut : ```""``` pour le ```string```, ```0``` pour l'```int```, ```07/01/2020 00:00:00``` pour le ```DateTime```, +à cause des lignes +```csharp +modelBuilder.Entity().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow); +modelBuilder.Entity().Property(n => n.Nom).HasDefaultValue(""); +``` +ajoutées à ```OnModelCreating```. + +```csharp +using (NounoursDBEntities db = new NounoursDBEntities()) +{ + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + + WriteLine("\nDisplay the last Nounours with default values"); + Nounours e = db.NounoursSet.ToList().Last(); + string nameStr = e.Nom != null ? e.Nom : "null"; + WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}; Insertion date: {e.DateDInsertion}; LastModified: {e.DernièreModification}") ; +} +``` + +## 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_007_ValueGeneration +``` + *Note*: + si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également : + +* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui), + +* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0. + +* Migration : +``` +dotnet ef migrations add migration_ex_042_007 +``` + * 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_007_ValueGeneration**. + + * Le résultat de l'exécution ressemblera à : +``` +database after cleaning and adding 3 Nounours and saving changes : + 49102173-b82a-4b4d-1d02-08d793b46eb3: Chewbacca (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05) + ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05) + ccee4eaf-4b50-48ec-1d04-08d793b46eb3: (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05) +Waits 3 seconds... + +Modification of the name of Chewbacca to Chewie + 49102173-b82a-4b4d-1d02-08d793b46eb3: Chewie (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:08) + ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05) + ccee4eaf-4b50-48ec-1d04-08d793b46eb3: (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05) + +Display the last Nounours with default values +Name: ; BirthDate: 07/01/2020 00:00:00; Hair count: 0; Insertion date: 07/01/2020 20:59:05; LastModified: 07/01/2020 20:59:05 +``` +*Notez que la date de modification de Chewie est modifée entre les deux affichages.* + + * **Comment vérifier le contenu des bases de données SQL Server ?** +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*. + + +* 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_007_ValueGeneration.Nounours.mdf* + * puis *Tables* + * Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données* + + + * Vous devriez maintenant pouvoir voir les données suivantes dans le tableau : + + |ID |Nom|DateDeNaissance|NbPoils + |---|---|---|--- + |1|Chewbacca|27/05/1977 00:00:00|1234567 + |2|Yoda|21/05/1980 00:00:00|3 + |3|NULL|01/01/0001 00:00:00|0 + +*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.* \ No newline at end of file diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ex_042_004_ValueGeneration_conventions.csproj b/p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ex_042_007_ValueGeneration.csproj similarity index 100% rename from p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ex_042_004_ValueGeneration_conventions.csproj rename to p08_BDD_EntityFramework/ex_042_007_ValueGeneration/ex_042_007_ValueGeneration.csproj