---
sidebar_label: '2.7. Value Generation'
sidebar_position: 7
description: "explique comment faire générer des valeurs automatiquement lors de l'insertion ou de la mise à jour"
---

# Value Generation
*07/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.

:::info 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 : [**1.1. Connection Strings**](../Entity-Framework/Fundamentals/ConnectionStrings) 
* les liens entre entités et tables : [**2.1. conventions d'écriture**](../Entity-Framework/Model/EF_CF_namingConventions), [**2.2. data annotations**](../Entity-Framework/Model/EF_CF_dataAnnotations) et [**2.3. Fluent API**](../Entity-Framework/Model/EF_CF_FluentAPI)  
  
Il est conseillé d'avoir également lu les exemples sur les clés primaires : [**2.4. Keys with conventions**](../Entity-Framework/Model/EF_CF_KeysConvention), [**2.5. Keys with data annotations**](../Entity-Framework/Model/EF_CF_KeysDataAnnotations) et [**2.6. Keys with Fluent API**](../Entity-Framework/Model/EF_CF_Keys_FluentAPI).
:::
  
---

## La génération de valeurs
**Entity Framework Core** propose trois solutions de génération de valeurs :
* **None** : c'est à l'utilisateur de donner une valeur avant l'insertion, sinon, la valeur par défaut du type est utilisée.  
Par défaut, c'est le mode utilisé pour toutes les propriétés, sauf les clés primaires. C'est donc uniquement dans ce cas qu'on devra chercher à l'utiliser si on veut gérer soi-même les valeurs des clés.
* **Generated on add** : on demande à la base de générer une valeur lors de l'insertion en base d'un nouvel élément.  
Pour savoir si l'élément est nouveau, **EF Core** regarde si l'entité était déjà en base ou non. Si la valeur de la propriété de la clé primaire à générer est la valeur par défaut (par exemple, ```null``` pour ```string```, ```0```pour ```int```, ```Guid.Empty``` pour ```Guid```...), une nouvelle valeur est générée, sinon, rien n'est changé.  
Ce mode est souvent utilisé pour les clés primaires ou les dates.
* **Generated on add or update** : on demande à la base de générer une valeur lors de l'insertion ou de la mise à jour de l'élément en base.  
Ce mode est souvent utilisé pour les dates représentant des mises à jour.  
  
Néanmoins, je trouve que l'utilisation sur des propriétés autres que les clés primaires ou avec des types différents de ```int``` ou ```Guid``` n'est pas simple et varie beaucoup en fonction des fournisseurs.
De plus, je pense que la génération de valeurs n'est généralement utilisée que pour des clés primaires, des dates (date de création, date de dernière modification) ou des *timestamps* (mais dans ce cas, d'autres mécanismes sont prévus).  
C'est la raison pour laquelle je pense qu'il est plus simple de se contenter de la réécriture de la méthode ```SaveChanges``` de votre contexte dérivant de ```DbContext```. 
Cet exemple propose cette méthode à travers l'ajout d'une date d'insertion et d'une date de dernière modification. 

## La classe ```Nounours```
La classe ```Nounours``` utilise dans cet exemple les *data annotations*.  
Notez l'ajout des propriétés ```DateDInsertion``` et ```DernièreModification```, sans annotations.
```csharp title='Nounours.cs'
[Table("TableNounours")]
public class Nounours
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid UniqueId
    {
        get; set;
    }

    [Required]
    [MaxLength(256)]
    public string Nom
    {
        get;
        set;
    }

    [Column("Naissance", TypeName = "date")]
    public DateTime DateDeNaissance
    {
        get;
        set;
    }

    public int NbPoils
    {
        get;
        set;
    }

    public DateTime DateDInsertion
    {
        get; set;
    }

    public DateTime DernièreModification
    {
        get; set;
    }
}
```  

## La classe NounoursDBEntites

### Gestion des valeurs par défaut pour les dates
Dans la méthode ```OnModelCreating``` de ```NounoursDBEntites```, on ajoute deux lignes permettant de donner une valeur par défaut aux deux dates au cas où l'utilisateur de les rentrerait pas :
```csharp title='NounoursDBEntities.cs'
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasDefaultValue("");
    modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow);
    modelBuilder.Entity<Nounours>().Property(n => n.DateDInsertion).HasDefaultValue(DateTime.UtcNow);
    modelBuilder.Entity<Nounours>().Property(n => n.DernièreModification).HasDefaultValue(DateTime.UtcNow);

    base.OnModelCreating(modelBuilder);
}
```  
:::note Remarque 
Ceci n'est pas obligatoire pour la génération de valeurs, c'est juste cadeau en passant. Le paragraphe suivant écrase ceci, sauf pour la ```DateDeNaissance```.
:::

### Génération des valeurs par la base
Dans cette méthode, je décide de réécrire la méthode virtuelle ```SaveChanges``` de ```NounoursDBEntities```.
```csharp title='NounoursDBEntities.cs'
public override int SaveChanges()
{
    ChangeTracker.DetectChanges();

    foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Added))
    {
        item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow;
        item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
    }

    foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Modified))
    {
        item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
    }
    return base.SaveChanges();
}
```
  * J'utilise dans cette méthode la propriété ```ChangeTracker``` de ```DbContext``` qui permet de garder la trace des modifications qui sont effectuées localement sur les entités avant la sauvegarde (update, delete, create...).
  L'appel de la méthode ```DetectChanges()``` permet de garder la trace de toutes les modifications réalisées sur les instances.
  * ```ChangeTracker``` me donne ainsi l'accès aux états des entités, de type ```EntityState```.
  * La première requête LINQ me donne accès aux entités de type ```Nounours``` ayant été ajoutées sans être encore sauvegardées.
```csharp
ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Added)
```  
Je récupère ainsi ces entités et modifie la valeur de leurs propriétés ```DateDInsertion``` et ```DernièreModification``` à maintenant (```DateTime.UtcNow```).
```csharp
item.Property(n => n.DateDInsertion).CurrentValue = DateTime.UtcNow;
item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
```
* Je fais ensuite la même chose en récupérant les entités ayant été mises à jour, c'est-à-dire dont la propriété ```State``` vaut ```EntityState.Modified```, et je modifie leur propriété ```DernièreModification```.
```csharp
foreach (var item in ChangeTracker.Entries<Nounours>().Where(e => e.State == EntityState.Modified))
{
    item.Property(n => n.DernièreModification).CurrentValue = DateTime.UtcNow;
}
```
* Enfin, j'appelle la méthode ```SaveChanges``` de la classe mère.  
  
En résumé, les valeurs ne sont pas générées sur la base, mais... on s'en fiche non ?

## La classe ```Program```
Cette classe est le point d'entrée du programme :
* Elle crée des instances de ```Nounours```  
Notez que la dernière ne donne aucune valeur pour les propriétés pour tester les valeurs par défaut.
```csharp title='Program.cs'
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 title='Program.cs'
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.  
:::caution note
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 title='Program.cs'
//nettoyage de la base de données
if (db.NounoursSet.Count() > 0)
{
    foreach (var n in db.NounoursSet)
    {
        db.NounoursSet.Remove(n);
    }
    db.SaveChanges();
}

//ajout des nounours dans la base de données
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.SaveChanges();

WriteLine("database after cleaning and adding 3 Nounours and saving changes :");
foreach (var n in db.NounoursSet)
{
    WriteLine($"\t{n}");
}
```
* Elle attend ensuite 3 secondes (pour qu'on puisse voir la différence dans les dates entre la mise à jour et l'ajout), puis modifie le nom du premier ```Nounours```
```csharp title='Program.cs'
WriteLine("Waits 3 seconds...");
System.Threading.Thread.Sleep(3000);

using (NounoursDBEntities db = new NounoursDBEntities())
{
    //modification d'un Nounours existant
    WriteLine("\nModification of the name of Chewbacca to Chewie");
    chewie = db.NounoursSet.First();
    chewie.Nom = "Chewie";
    db.SaveChanges();
}
```
* Enfin, elle affiche les ```Nounours```de la base une nouvelle fois, et les valeurs des propriétés du dernier Nounours pour montrer les valeurs par défaut : ```""``` pour le ```string```, ```0``` pour l'```int```, ```07/01/2020 00:00:00``` pour le ```DateTime```, 
à cause des lignes 
```csharp title='Program.cs'
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow);
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasDefaultValue("");
```
ajoutées à ```OnModelCreating```.  

```csharp title='Program.cs'
using (NounoursDBEntities db = new NounoursDBEntities())
{
    foreach (var n in db.NounoursSet)
    {
        WriteLine($"\t{n}");
    }

    WriteLine("\nDisplay the last Nounours with default values");
    Nounours e = db.NounoursSet.ToList().Last();
    string nameStr = e.Nom != null ? e.Nom : "null";
    WriteLine($"Name: {nameStr}; BirthDate: {e.DateDeNaissance}; Hair count: {e.NbPoils}; Insertion date: {e.DateDInsertion}; LastModified: {e.DernièreModification}") ;
}
```  

## Comment exécuter cet exemple ?
Pour tester cette application, n'oubliez pas les commandes comme présentées dans l'exemple ex_041_001 : pour générer l'exemple, il vous faut d'abord préparer les migrations et les tables.
  * Ouvrez la *Console du Gestionnaire de package*, pour cela, dirigez-vous dans le menu *Outils*, puis *Gestionnaire de package NuGet*, puis *Console du Gestionnaire de package*.
  * Dans la console que vous venez d'ouvrir, déplacez-vous dans le dossier du projet .NET Core, ici :
```
cd .\p08_BDD_EntityFramework\ex_042_007_ValueGeneration
``` 
:::note
  si vous n'avez pas installé correctement EntityFrameworkCore, il vous faudra peut-être utiliser également :

* ```dotnet tool install --global dotnet-ef``` si vous utilisez la dernière version de .NET Core (3.1 aujourd'hui),  

* ```dotnet tool install --global dotnet-ef --version 3.0.0``` si vous vous utiliser spécifiquement .NET Core 3.0.
:::

* Migration : 
```
dotnet ef migrations add migration_ex_042_007
```
  * Création de la table :
```
dotnet ef database update
```
  * Génération et exécution
Vous pouvez maintenant générer et exécuter l'exemple **ex_042_007_ValueGeneration**.

  * Le résultat de l'exécution ressemblera à :
```
database after cleaning and adding 3 Nounours and saving changes :
        49102173-b82a-4b4d-1d02-08d793b46eb3: Chewbacca (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
        ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
        ccee4eaf-4b50-48ec-1d04-08d793b46eb3:  (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
Waits 3 seconds...

Modification of the name of Chewbacca to Chewie
        49102173-b82a-4b4d-1d02-08d793b46eb3: Chewie (27/05/1977, 1234567 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:08)
        ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3: Yoda (21/05/1980, 3 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)
        ccee4eaf-4b50-48ec-1d04-08d793b46eb3:  (07/01/2020, 0 poils, Inserted on: 07/01/2020 20:59:05, Last modified on: 07/01/2020 20:59:05)

Display the last Nounours with default values
Name: ; BirthDate: 07/01/2020 00:00:00; Hair count: 0; Insertion date: 07/01/2020 20:59:05; LastModified: 07/01/2020 20:59:05
```
:::note
Notez que la date de modification de Chewie est modifée entre les deux affichages.
:::

  * **Comment vérifier le contenu des bases de données SQL Server ?**
Vous pouvez vérifier le contenu de votre base en utilisant l'*Explorateur d'objets SQL Server*.
* Pour cela, allez dans le menu *Affichage* puis *Explorateur d'objets SQL Server*.  
![Sql Server](../Fundamentals/ConnectionStringsFiles/sqlserver_01.png)   

* Déployez dans l'*Explorateur d'objets SQL Server* :
  *  *SQL Server*, 
  *  puis *(localdb)\MSSQLLocalDB ...*, 
  *  puis *Bases de données*
  *  puis celle portant le nom de votre migration, dans mon cas : *ex_042_007_ValueGeneration.Nounours.mdf*
  *  puis *Tables*   
  *  Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les données*  
![Sql Server](../Fundamentals/ConnectionStringsFiles/sqlserver_02.png)   

  *  Vous devriez maintenant pouvoir voir les données suivantes dans le tableau :  
 
  |UniqueId   |Nom|Naissance|NbPoils|DateDInsertion|DernièreModification
  |---|---|---|---|---|---
  |49102173-b82a-4b4d-1d02-08d793b46eb3|Chewbacca|27/05/1977|1234567|07/01/2020 20:59:05|07/01/2020 20:59:08
  |ae2ab1fa-ac8d-4df9-1d03-08d793b46eb3|Yoda|21/05/1980|3|07/01/2020 20:59:05|07/01/2020 20:59:05
  |ccee4eaf-4b50-48ec-1d04-08d793b46eb3||07/01/2020|0|07/01/2020 20:59:05|07/01/2020 20:59:05
:::note 
Les identifiants seront bien sûr différents.
:::