From b01010359e2f8b6be947d4defe346c471c0f113f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Chevaldonn=C3=A9?= Date: Sun, 5 Jan 2020 22:01:30 +0100 Subject: [PATCH] begun some samples about ValueGeneration; to be finished --- Exemples.sln | 48 +++- .../ReadMe.md | 2 +- .../Nounours.cs | 102 ++++++++ .../NounoursDBEntities.cs | 57 ++++ .../Program.cs | 81 ++++++ .../ReadMe.md | 247 ++++++++++++++++++ .../dbo.Trigger.sql | 18 ++ ...05_ValueGeneration_data_annotations.csproj | 12 + .../Nounours.cs | 93 +++++++ .../NounoursDBEntities.cs | 78 ++++++ .../Program.cs | 89 +++++++ ...x_042_006_ValueGeneration_FluentAPI.csproj | 12 + 12 files changed, 837 insertions(+), 2 deletions(-) create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/dbo.Trigger.sql create mode 100644 p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj create mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs create mode 100644 p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj diff --git a/Exemples.sln b/Exemples.sln index 350e5fb..9b0fc21 100644 --- a/Exemples.sln +++ b/Exemples.sln @@ -558,7 +558,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CF_Fluent_API EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Keys and value generation", "02. Keys and value generation", "{5B333C02-67B7-4A4C-AA58-2710C183292B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_004_ValueGeneration_conventions", "p08_BDD_EntityFramework\ex_042_004_ValueGeneration_conventions\ex_042_004_ValueGeneration_conventions.csproj", "{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_004_ValueGeneration_conventions", "p08_BDD_EntityFramework\ex_042_004_ValueGeneration_conventions\ex_042_004_ValueGeneration_conventions.csproj", "{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_005_ValueGeneration_data_annotations", "p08_BDD_EntityFramework\ex_042_005_ValueGeneration_data_annotations\ex_042_005_ValueGeneration_data_annotations.csproj", "{87EF3DAF-4166-496F-B0CE-546E20FADBAC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_006_ValueGeneration_FluentAPI", "p08_BDD_EntityFramework\ex_042_006_ValueGeneration_FluentAPI\ex_042_006_ValueGeneration_FluentAPI.csproj", "{BAB08578-898E-48C5-8470-6AC72D49B0D8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -4636,6 +4640,46 @@ Global {0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x64.Build.0 = Release|Any CPU {0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x86.ActiveCfg = Release|Any CPU {0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x86.Build.0 = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|ARM.Build.0 = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|ARM64.Build.0 = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|x64.ActiveCfg = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|x64.Build.0 = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|x86.ActiveCfg = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Debug|x86.Build.0 = Debug|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|Any CPU.Build.0 = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|ARM.ActiveCfg = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|ARM.Build.0 = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|ARM64.ActiveCfg = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|ARM64.Build.0 = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|x64.ActiveCfg = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|x64.Build.0 = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|x86.ActiveCfg = Release|Any CPU + {87EF3DAF-4166-496F-B0CE-546E20FADBAC}.Release|x86.Build.0 = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|ARM.ActiveCfg = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|ARM.Build.0 = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|ARM64.Build.0 = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|x64.Build.0 = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Debug|x86.Build.0 = Debug|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|Any CPU.Build.0 = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|ARM.ActiveCfg = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|ARM.Build.0 = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|ARM64.ActiveCfg = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|ARM64.Build.0 = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x64.ActiveCfg = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x64.Build.0 = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x86.ActiveCfg = Release|Any CPU + {BAB08578-898E-48C5-8470-6AC72D49B0D8}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4901,6 +4945,8 @@ Global {9D444878-F974-4883-9023-1A46E80ADFF1} = {FC04E822-7560-4C80-9E59-C03DB40E9F17} {5B333C02-67B7-4A4C-AA58-2710C183292B} = {55E00151-58A6-4E7D-9457-0BB8213B82F5} {0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3} = {5B333C02-67B7-4A4C-AA58-2710C183292B} + {87EF3DAF-4166-496F-B0CE-546E20FADBAC} = {5B333C02-67B7-4A4C-AA58-2710C183292B} + {BAB08578-898E-48C5-8470-6AC72D49B0D8} = {5B333C02-67B7-4A4C-AA58-2710C183292B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E} diff --git a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md index 92c95c1..9d1918c 100644 --- a/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md +++ b/p08_BDD_EntityFramework/ex_042_004_ValueGeneration_conventions/ReadMe.md @@ -11,7 +11,7 @@ Pour plus de renseignements sur : * 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_DataAnnotations**. +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**. --- 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 new file mode 100644 index 0000000..6f7f68d --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Nounours.cs @@ -0,0 +1,102 @@ +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; + } = DateTime.Now; + + /// + /// 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 new file mode 100644 index 0000000..4fea1de --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/NounoursDBEntities.cs @@ -0,0 +1,57 @@ +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 new file mode 100644 index 0000000..723c570 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/Program.cs @@ -0,0 +1,81 @@ +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 new file mode 100644 index 0000000..d62d872 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ReadMe.md @@ -0,0 +1,247 @@ +# 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_005_ValueGeneration_data_annotations/dbo.Trigger.sql b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/dbo.Trigger.sql new file mode 100644 index 0000000..4e9dd71 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/dbo.Trigger.sql @@ -0,0 +1,18 @@ +CREATE TRIGGER [dbo].[InsertionDateTrigger] + ON [dbo].[TableNounours] + FOR 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() + WHERE UniqueId = @Id +END \ 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_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj new file mode 100644 index 0000000..44a96d3 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_ValueGeneration_data_annotations/ex_042_005_ValueGeneration_data_annotations.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + + diff --git a/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs new file mode 100644 index 0000000..655c1e8 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Nounours.cs @@ -0,0 +1,93 @@ +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 new file mode 100644 index 0000000..f2355db --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/NounoursDBEntities.cs @@ -0,0 +1,78 @@ +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/Program.cs b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs new file mode 100644 index 0000000..c75f7d9 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/Program.cs @@ -0,0 +1,89 @@ +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using static System.Console; + +namespace ex_042_006_ValueGeneration_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 (); + + 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("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.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}; Insertion date: {e.InsertionDate}; LastModified: {e.LastModified}") ; + } + } + 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_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj new file mode 100644 index 0000000..d82bdf5 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_006_ValueGeneration_FluentAPI/ex_042_006_ValueGeneration_FluentAPI.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + +