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
+
+
+
+
+
+
+