begun some samples about ValueGeneration; to be finished

EFCore3_Reforged
Marc CHEVALDONNE 6 years ago
parent 35ff4302cf
commit b01010359e

@ -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}

@ -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**.
---

@ -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;
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on:{InsertionDate}, Last modified on: {LastModified})";
}
}
}

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_005_ValueGeneration_data_annotations
{
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> 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";
}
}

@ -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();
}
}
}

@ -0,0 +1,247 @@
# ex_042_005_ValueGeneration_data_annotations
*05/01/2020 &sdot; 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*.
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_01.png" width="500"/>
* Déployez dans l'*Explorateur d'objets SQL Server* :
* *SQL Server*,
* puis *(localdb)\MSSQLLocalDB ...*,
* puis *Bases de données*
* puis celle portant le nom de votre migration, dans mon cas : *ex_042_004_ValueGeneration_conventions.Nounours.mdf*
* puis *Tables*
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*
<img src="../ex_041_001_ConnectionStrings/readmefiles/sqlserver_02.png" width="460"/>
* 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.*

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

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>

@ -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;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
public override bool Equals(object right)
{
//check null
if (object.ReferenceEquals(right, null))
{
return false;
}
if (object.ReferenceEquals(this, right))
{
return true;
}
if (this.GetType() != right.GetType())
{
return false;
}
return this.Equals(right as Nounours);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils, Inserted on: {InsertionDate}, Last modified on: {LastModified})";
}
}
}

@ -0,0 +1,78 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
namespace ex_042_006_ValueGeneration_FluentAPI
{
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> 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<Nounours>().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<Nounours>().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<Nounours>().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<Nounours>().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<Nounours>().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<Nounours>().Ignore(n => n.NbPoils);
modelBuilder.Entity<Nounours>().Property(n => n.InsertionDate)/*.ValueGeneratedOnAdd()*/.HasDefaultValue(DateTime.UtcNow);
modelBuilder.Entity<Nounours>().Property(n => n.LastModified)/*.ValueGeneratedOnAddOrUpdate()*/.HasDefaultValue(DateTime.UtcNow);
//if (this.Database.IsSqlServer())
//{
// modelBuilder.Entity<Nounours>().Property(n => n.InsertionDate).HasDefaultValueSql("getdate()");
// modelBuilder.Entity<Nounours>().Property(n => n.LastModified).HasDefaultValueSql("getdate()");
//}
base.OnModelCreating(modelBuilder);
}
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
foreach (var item in ChangeTracker.Entries<Nounours>().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<Nounours>().Where(e => e.State == EntityState.Modified))
{
item.Property(n => n.LastModified).CurrentValue = DateTime.UtcNow;
}
return base.SaveChanges();
}
}
}

@ -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();
}
}
}

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
</ItemGroup>
</Project>
Loading…
Cancel
Save