From 6565548bdbd9f7766f0105be47d97eb0cecb53c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Chevaldonn=C3=A9?= Date: Fri, 3 Jan 2020 23:23:13 +0100 Subject: [PATCH] finished sample ex_042_003 --- Exemples.sln | 78 +---- p08_BDD_EntityFramework/ReadMe.md | 4 +- .../ReadMe.md | 4 +- .../ex_042_003_EF_CF_Fluent_API/Program.cs | 62 +++- .../ex_042_003_EF_CF_Fluent_API/ReadMe.md | 299 +++++++++++++++++- 5 files changed, 366 insertions(+), 81 deletions(-) diff --git a/Exemples.sln b/Exemples.sln index 1b34ecc..2eafa1f 100644 --- a/Exemples.sln +++ b/Exemples.sln @@ -98,8 +98,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "p08_BDD_EntityFramework", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap042_EntityFramework", "chap042_EntityFramework", "{ED951AD1-2EB1-49CC-9DBE-E3360E11525B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. EF CodeFirst approach", "01. EF CodeFirst approach", "{D2D113F6-9444-4F75-959F-8761054F1AEE}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Initialization Strategy and Seeding Data with CodeFirst", "02. Initialization Strategy and Seeding Data with CodeFirst", "{41B79FDB-6371-41E1-B9A3-73A9A209E906}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03. Entity relationships with CodeFirst", "03. Entity relationships with CodeFirst", "{593200F9-6D14-43BC-9289-8BB75FAC6552}" @@ -414,12 +412,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_039_002_LINQ_to_XML", "e EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_040_001_LINQ_to_Json", "ex_040_001_LINQ_to_Json\ex_040_001_LINQ_to_Json.csproj", "{B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_001_EF_CodeFirst_conventions", "ex_042_001_EF_CodeFirst_conventions\ex_042_001_EF_CodeFirst_conventions.csproj", "{515F8C64-9070-4110-B3E0-092379DF6444}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_002_EF_CodeFirst_data_annotations", "ex_042_002_EF_CodeFirst_data_annotations\ex_042_002_EF_CodeFirst_data_annotations.csproj", "{970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CodeFirst_Fluent_API", "ex_042_003_EF_CodeFirst_Fluent_API\ex_042_003_EF_CodeFirst_Fluent_API.csproj", "{FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_EF_CF_InitializationStrategy", "ex_042_004_EF_CF_InitializationStrategy\ex_042_004_EF_CF_InitializationStrategy.csproj", "{C25760E4-A043-4BB9-8A8D-2907CCB810FA}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_005_EF_CF_Seeding_Data", "ex_042_005_EF_CF_Seeding_Data\ex_042_005_EF_CF_Seeding_Data.csproj", "{0B2BAB0F-36F8-4E56-AA52-309DF1CE0302}" @@ -558,11 +550,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "chap042_EF_Creating_Model", EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01. EF CodeFirst approach", "01. EF CodeFirst approach", "{FC04E822-7560-4C80-9E59-C03DB40E9F17}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_001_EF_CF_conventions", "p08_BDD_EntityFramework\ex_042_001_EF_CF_conventions\ex_042_001_EF_CF_conventions.csproj", "{1566457E-6D28-412C-86A3-95E8821B2DDD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_001_EF_CF_conventions", "p08_BDD_EntityFramework\ex_042_001_EF_CF_conventions\ex_042_001_EF_CF_conventions.csproj", "{1566457E-6D28-412C-86A3-95E8821B2DDD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_002_EF_CF_data_annotations", "p08_BDD_EntityFramework\ex_042_002_EF_CF_data_annotations\ex_042_002_EF_CF_data_annotations.csproj", "{568486B5-85E4-4F86-B86C-A373B5F471FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_002_EF_CF_data_annotations", "p08_BDD_EntityFramework\ex_042_002_EF_CF_data_annotations\ex_042_002_EF_CF_data_annotations.csproj", "{568486B5-85E4-4F86-B86C-A373B5F471FD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_003_EF_CF_Fluent_API", "p08_BDD_EntityFramework\ex_042_003_EF_CF_Fluent_API\ex_042_003_EF_CF_Fluent_API.csproj", "{9D444878-F974-4883-9023-1A46E80ADFF1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CF_Fluent_API", "p08_BDD_EntityFramework\ex_042_003_EF_CF_Fluent_API\ex_042_003_EF_CF_Fluent_API.csproj", "{9D444878-F974-4883-9023-1A46E80ADFF1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -3470,66 +3462,6 @@ Global {B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x64.Build.0 = Release|Any CPU {B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.ActiveCfg = Release|Any CPU {B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3}.Release|x86.Build.0 = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|Any CPU.Build.0 = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM.ActiveCfg = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM.Build.0 = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|ARM64.Build.0 = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x64.ActiveCfg = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x64.Build.0 = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x86.ActiveCfg = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Debug|x86.Build.0 = Debug|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|Any CPU.ActiveCfg = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|Any CPU.Build.0 = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM.ActiveCfg = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM.Build.0 = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM64.ActiveCfg = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|ARM64.Build.0 = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|x64.ActiveCfg = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|x64.Build.0 = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|x86.ActiveCfg = Release|Any CPU - {515F8C64-9070-4110-B3E0-092379DF6444}.Release|x86.Build.0 = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM.ActiveCfg = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM.Build.0 = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|ARM64.Build.0 = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x64.ActiveCfg = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x64.Build.0 = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x86.ActiveCfg = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Debug|x86.Build.0 = Debug|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|Any CPU.Build.0 = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM.ActiveCfg = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM.Build.0 = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM64.ActiveCfg = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|ARM64.Build.0 = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x64.ActiveCfg = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x64.Build.0 = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x86.ActiveCfg = Release|Any CPU - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC}.Release|x86.Build.0 = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM.ActiveCfg = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM.Build.0 = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|ARM64.Build.0 = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x64.ActiveCfg = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x64.Build.0 = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x86.ActiveCfg = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Debug|x86.Build.0 = Debug|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|Any CPU.Build.0 = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM.ActiveCfg = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM.Build.0 = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM64.ActiveCfg = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|ARM64.Build.0 = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x64.ActiveCfg = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x64.Build.0 = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x86.ActiveCfg = Release|Any CPU - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC}.Release|x86.Build.0 = Release|Any CPU {C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {C25760E4-A043-4BB9-8A8D-2907CCB810FA}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -4722,7 +4654,6 @@ Global {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5} = {1BCDE8FE-64B4-4C41-9323-81BB09EFD7CD} {3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0} = {1BCDE8FE-64B4-4C41-9323-81BB09EFD7CD} {ED951AD1-2EB1-49CC-9DBE-E3360E11525B} = {65DF0C9A-4F06-4361-8DD9-E8360B6325CA} - {D2D113F6-9444-4F75-959F-8761054F1AEE} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B} {41B79FDB-6371-41E1-B9A3-73A9A209E906} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B} {593200F9-6D14-43BC-9289-8BB75FAC6552} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B} {663523E4-3B60-4534-876F-8BD77110E6D8} = {ED951AD1-2EB1-49CC-9DBE-E3360E11525B} @@ -4877,9 +4808,6 @@ Global {CF1AE501-3518-45CC-94AA-D6DE047EA022} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5} {26400B99-2FB6-4B5F-BE2B-D15124F2B51D} = {13CDB6B1-9B1D-43A9-9E9E-08989D55ACA5} {B506ECD8-853D-4E69-8F0A-AFCDB7FEABF3} = {3BD95300-E2F6-4CA6-B4CC-5D19DF5C6AC0} - {515F8C64-9070-4110-B3E0-092379DF6444} = {D2D113F6-9444-4F75-959F-8761054F1AEE} - {970EBC79-B6F4-4E07-89E5-5C5D18AF09BC} = {D2D113F6-9444-4F75-959F-8761054F1AEE} - {FAE65E56-8B71-4A54-A20A-586B7A8D8CCC} = {D2D113F6-9444-4F75-959F-8761054F1AEE} {C25760E4-A043-4BB9-8A8D-2907CCB810FA} = {41B79FDB-6371-41E1-B9A3-73A9A209E906} {0B2BAB0F-36F8-4E56-AA52-309DF1CE0302} = {41B79FDB-6371-41E1-B9A3-73A9A209E906} {0770D7BB-F7FA-4E44-900C-F72F33110AF9} = {593200F9-6D14-43BC-9289-8BB75FAC6552} diff --git a/p08_BDD_EntityFramework/ReadMe.md b/p08_BDD_EntityFramework/ReadMe.md index f1a8bad..6b66a5f 100644 --- a/p08_BDD_EntityFramework/ReadMe.md +++ b/p08_BDD_EntityFramework/ReadMe.md @@ -1,5 +1,5 @@ # Entity Framework Core 3.0 -*02/01/2020 ⋅ Marc Chevaldonné* +*03/01/2020 ⋅ Marc Chevaldonné* --- Entity Framework (EF) Core est un ORM (Object-Relational Mapper) qui permet aux développeurs .NET de gérer de manière simple, légère et extensible, des bases de données. @@ -24,6 +24,8 @@ Ce chapitre s'attardera sur le lien entre le mod Je présenterai en conséquence tout d'abord comment écrire des classes pour votre modèle, puis comment écrire les différentes relations classiques (aggrégation, *one to one*, *one to many*, *many to many*, mais aussi les dictionnaires), comment gérer les héritages entre classes du modèle dans la base de données, etc. * **ex_042_001 : conventions d'écriture** : explique quelles sont les conventions d'écriture utilisées pour la transformation d'une entité en table. * **ex_042_002 : data annotations** : explique comment utiliser les *data annotations* pour personnaliser la transformation d'une entité en table. + * **ex_042_003 : Fluent API** : explique comment utiliser la *Fluent API* pour personnaliser la transformation d'une entité en table. + 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. 4. *Querying (LINQ to SQL) and saving data* : 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 89861b7..6374883 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 @@ -1,4 +1,4 @@ -# ex_042_002_EF_CF_data_annotations_ +# ex_042_002_EF_CF_data_annotations *03/01/2020 ⋅ Marc Chevaldonné* --- @@ -178,7 +178,7 @@ public string Nom set; } ``` -* L'annotation ```[MaxLength(256)]``` permet d'imposer une taille fixe à une chaîne de caractères. Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères. +* L'annotation ```[MaxLength(256)]``` permet d'imposer une taille max à une chaîne de caractères. Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères. ```csharp [MaxLength(256)] public string Nom diff --git a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Program.cs b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Program.cs index 835aa9e..0c1e03b 100644 --- a/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Program.cs +++ b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/Program.cs @@ -1,4 +1,7 @@ -using System; +using Microsoft.Data.SqlClient; +using System; +using System.Linq; +using static System.Console; namespace ex_042_003_EF_CF_Fluent_API { @@ -6,7 +9,62 @@ namespace ex_042_003_EF_CF_Fluent_API { static void Main(string[] args) { - Console.WriteLine("Hello World!"); + 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 }; + + try + { + using (NounoursDBEntities db = new NounoursDBEntities()) + { + if (db.NounoursSet.Count() > 0) + { + WriteLine("La base n'est pas vide !"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + WriteLine("début du nettoyage..."); + + foreach (var n in db.NounoursSet) + { + WriteLine($"Suppression de {n}"); + db.NounoursSet.Remove(n); + } + + WriteLine("Base avant sauvegarde des changements :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + db.SaveChanges(); + WriteLine("Base après sauvegarde des changements :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + } + + db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); + + db.SaveChanges(); + WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + + } + } + 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_003_EF_CF_Fluent_API/ReadMe.md b/p08_BDD_EntityFramework/ex_042_003_EF_CF_Fluent_API/ReadMe.md index 5f28270..3d6ce42 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 @@ -1 +1,298 @@ - \ No newline at end of file +# ex_042_003_EF_CF_Fluent_API +*03/01/2020 ⋅ Marc Chevaldonné* + +--- + +Le lien entre une base de données et vos classes à l'aide de **Entity Framework Core** se fait via une classe dérivant de ```DbContext```. +Cette classe doit contenir des ```DbSet```. Chaque ```DbSet``` correspond à une table et ```T``` correspond à une de vos classes qu'on appelle *entité*. +Le lien entre les tables et les entités est géré plus ou moins automatiquement par le *framework*. +**EF Core** permet de lire/écrire les instances d'entité de la base de données ; permet de créer des tables pour les entités via les migrations (pour les bases de données relationnelles) ; +les types exposés dans les propriétés d'une entité deviennent automatiquement des entités (mais pas forcément des tables) + +**Entity Framework Core** propose 3 solutions différentes pour relier des entités à des tables de la base de données : +* **conventions d'écriture** : c'est la solution la plus simple, qui analyse le code de votre classe pour en déduire de quelle manière la relier à la table. +* **data annotations** : elle se base sur des décorateurs (appelés *data annotations*) que vous placez autour de vos propriétés pour indiquer de quelle manière les relier aux colonnes de votre table. Les *data annotations* écrasent les conventions d'écriture. +* **Fluent API** : directement dans le code de votre ```DbContext``` (plus précisément, dans la méthode ```OnModelCreating```), vous précisez comment se fait le *mapping* entre votre entité et votre table. La *Fluent API* écrase les conventions d'écriture et les *data annotations*. + +En d'autres termes, si vous n'écrivez rien, **EF Core** utilise les conventions d'écriture ; si vous n'utilisez que des *data annotations*, elles sont prioritaires sur les convetions d'écriture ; si vous utilisez la *Fluent API*, elle est prioritaire sur les deux autres méthodes. +Mais on peut faire un mix des différentes méthodes. + +Cet exemple montre de quelle manière la **Fluent API** est utilisée pour transformer une entité en table. +Il montre notamment : +* comment choisir le nom de la table +* comment ignorer une propriété de l'entité +* comment le nom et le type d'une colonne de la table peuvent être modifiés +* comment un identifiant unique est choisi et généré. + +Cette méthode est préférable dans les deux cas principaux suivants (et en particulier le 1er) : +* nous n'avons pas accès au code source de la classe Nounours ou bien nous n'avons pas le droit de le modifier, et les conventions d'écriture ne +correspondent pas à ce que nous souhaitons réaliser. +* nous ne souhaitons pas "polluer" la classe POCO avec des annotations de données. +L'inconvénient majeur et évident de cette méthode est que la lecture et la maintenance sont plus compliquées. + +Cet exemple est répété d'une manière très similaire en utilisant les autres méthodes de *mapping* entre entité et table : +* **ex_042_001_EF_CF_conventions** : avec les *conventions d'écriture* +* **ex_042_002_EF_CF_data_annotations** : avec les *data annotations* + +--- + +## Comment est construit cet exemple ? +* Le projet est de type .NET Core +* Il contient deux classes : + * ```Nounours``` + * ```NounoursDBEntities``` + +### La classe ```Nounours``` +* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object. +```csharp +public class Nounours +{ + public Guid UniqueId + { + get; set; + } + + public string Nom + { + get; + set; + } + + public DateTime DateDeNaissance + { + get; + set; + } + + public int NbPoils + { + get; + set; + } +} +``` +* Elle contient 4 propriétés en lecture/écriture : ```UniqueId```, ```Nom```, ```DateDeNaissance``` et ```NbPoils```. +* **Entity Framework Core** va utiliser la classe POCO ```Nounours``` pour créer une table dans la base de données, lorsque le ```DbSet``` va être créé. +* L'utilisation des conventions d'écriture d'Entity Framework pourrait s'appliquer car il n'y a pas de *data annotations* (cf. ex_042_002) ; mais comme la classe ```NounoursDbEntities``` utilise la *Fluent API*, elles seront écrasées. + +### La classe ```NounoursDBEntities``` +* Cette classe dérive de ```DbContext```. Elle contient des ```DbSet``` où ```T``` est une entité. Elle contient autant de ```DbSet``` que de tables. +```csharp +class NounoursDBEntities : DbContext +{ + public virtual DbSet NounoursSet { get; set; } +} +``` +ici, on indique donc qu'il y aura une table de ```Nounours```. +* **EF Core** utilisera cette classe pour faire la création de la base de données et de ses tables. Pour cela, elle utilise la chaîne de connexion donnée dans la méthode ```OnConfiguring```. +```csharp +protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) +{ + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_003_EF_CF_Fluent_API.Nounours.mdf;Trusted_Connection=True;"); +} +``` +=> cf **ex_041_001_ConnectionStrings** pour en savoir plus sur les chaîne de connexion +* Dans cet exemple, la table de ```Nounours``` est créée en utilisant la *Fluent API* et les conventions d'écriture de la classe ```Nounours```. + +* La **Fluent API** est utilisée à travers la méthode ```OnModelCreating```. +```csharp +protected override void OnModelCreating(ModelBuilder modelBuilder) +{ + modelBuilder.Entity().ToTable("TableNounours"); + + modelBuilder.Entity().HasKey(n => n.UniqueId); + + modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); + + modelBuilder.Entity().Property(n => n.Nom).IsRequired() + .HasMaxLength(256); + + modelBuilder.Entity().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date"); + + modelBuilder.Entity().Ignore(n => n.NbPoils); + + base.OnModelCreating(modelBuilder); +} +``` +L'instance de ```ModelBuilder``` permet ensuite d'atteindre les entités avec la méthode générique ```Entity()```où ```T```est l'entité. +A partir d'une entité, on peut ensuite atteindre une propriété à l'aide de la méthode ```Property(...)``` pouvant prendre en paramètre +soit le nom de la propriété, soit une expression lambda (recommandé pour éviter les erreurs de compilation). +Ensuite, à l'aide des méthodes d'extension, on peut définir des contraintes sur les entités et leurs propriétés. + + + +Si on prend chaque ligne de la méthode ```OnModelCreating``` en détail, on peut voir : +* le choix du nom de la table associée à l'entité ```Nounours``` +*équivalent du ```[Table("TableNounours")]``` avec les annotations de données* +```csharp +modelBuilder.Entity().ToTable("TableNounours"); +``` +* on peut changer le nom et le type d'une colonne avec les méthodes d'extension ```HasColumnName``` et ```HasColumnType``` sur la propriété de l'entité. +Dans l'exemple ci-dessous, la colonne ne s'appellera pas "DateDeNaissance" (comme le permettraient les conventions d'écriture), mais "Naissance" grâce à la *Fluent API* et son type sera ```date``` au lieu de ```datetime2(7)``` avec les conventions d'écriture. +*équivalent avec une annotation de ```[Column("Naissance", TypeName="date")]``` devant la propriété ```DateDeNaissance``` de ```Nounours```* +```csharp +modelBuilder.Entity().Property(n => n.DateDeNaissance) + .HasColumnName("Naissance") + .HasColumnType("date"); +``` +* on peut rendre une propriété obligatoire (les propriétés sont optionnelles par défaut si elles sont *nullable*) avec la méthode d'extension ```IsRequired()```. +Dans l'exemple ci-dessous, le nom est obligatoire. +*équivalent de ```[Required]``` devant la propriété ```Nom``` de ```Nounours```* +```csharp +modelBuilder.Entity().Property(n => n.Nom).IsRequired(); +``` +* on peut imposer une taille max à une chaîne de caractères avec la méthode d'extension ```HasMaxLength(...)```. +Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères. +*équivalent de ```[MaxLength(256)]``` devant la propriété ```Nom``` de ```Nounours```* +```csharp +modelBuilder.Entity().Property(n => n.Nom).HasMaxLength(256); +``` +* on peut chaîner les méthodes d'extension. +Dans l'exemple ci-dessous, le nom est obligatoire et ne doit pas avoir plus de 256 caractères. +```csharp +modelBuilder.Entity().Property(n => n.Nom) + .IsRequired() + .HasMaxLength(256); +``` +* on peut préciser qu'on ne veut pas qu'une propriété soit transformée en colonne de la table avec la méthode d'extension ```Ignore()```. +Par exemple, ici, le nombre de poils ne sera pas mappé en colonne. +*équivalent de ```[NotMapped]``` devant la propriété ```NbPoils``` de ```Nounours```* +```csharp +modelBuilder.Entity().Ignore(n => n.NbPoils); +``` +* on peut préciser qu'une propriété doit être utilisée en tant que clé primaire avec la méthode d'extension ```HasKey``` prenant en paramètre le nom de la propriété à prendre en compte. +*équivalent de ```[Key]``` devant la propriété à utiliser en clé primaire* +```csharp +modelBuilder.Entity().HasKey(n => n.UniqueId); +``` +On peut ensuite préciser qu'on veut que cette clé soit générée par la base lors de l'insertion à l'aide de la méthode d'extension ```ValueGeneratedOnAdd()```. +*équivalent de ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]``` devant la propriété à utiliser en clé primaire* +```csharp +modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); +``` +Notez que la clé n'est donc, contrairement aux conventions d'écriture, pas nécessairement un ```int```. Elle peut-être un ```Guid```, un ```string```, etc. Son nom peut-être autre chose que "ID". +*Note : on peut aussi faire des clés composées, comme je le montrerai dans un autre exemple.* +Dans l'exemple ci-dessous, un ```Nounours```possédera une clé unique de type ```Guid``` générée par la base et placée dans la colonne "UniqueId". +```csharp +modelBuilder.Entity().HasKey(n => n.UniqueId); +modelBuilder.Entity().Property(n => n.UniqueId).ValueGeneratedOnAdd(); +``` + +**En résumé**, avec la *Fluent API* : +* on peut changer le nom de la table +* on peut changer le nom et le type d'une colonne +* on peut rendre une propriété obligatoire ou optionnelle +* on peut empêcher le *mapping* d'une propriété +* on peut imposer une taille max pour les chaînes de caractères +* on peut transformer une propriété en clé primaire et demander à la base de la générée lors de l'insertion. + +### La classe ```Program``` +Cette classe est le point d'entrée du programme : +* Elle crée des instances de ```Nounours``` +```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 }; +``` +* 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. +Notez l'accès à la table de ```Nounours``` via ```db.NounoursSet```. +Notez également que tant que ```SaveChanges``` n'est pas appelée, les suppressions ne sont pas effectives dans la base, seulement en local dans le programme. +_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 +if (db.NounoursSet.Count() > 0) +{ + WriteLine("La base n'est pas vide !"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + WriteLine("début du nettoyage..."); + + foreach (var n in db.NounoursSet.ToArray()) + { + WriteLine($"Suppression de {n}"); + db.NounoursSet.Remove(n); + } + + WriteLine("Base avant sauvegarde des changements :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + db.SaveChanges(); + WriteLine("Base après sauvegarde des changements :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } +} +``` +* Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base. +```csharp +db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); + +db.SaveChanges(); +WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :"); +foreach (var n in db.NounoursSet) +{ + WriteLine($"\t{n}"); +} +``` + +## 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_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 +``` + + * Migration : +``` +dotnet ef migrations add migration_ex_042_003 +``` + * 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_003_EF_CF_Fluent_API**. + + * 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_003_EF_CF_Fluent_API.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 : + + |UniqueId |Nom|Naissance + |---|---|--- + |30817fff-8736-457d-db18-08d7908d7986|Chewbacca|27/05/1977 + |aa4469c4-a6c8-4077-db19-08d7908d7986|Yoda|21/05/1980 + |69cb5892-6750-4629-db1a-08d7908d7986|Ewok|25/05/1983 + +*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