---
sidebar_label: '2.6. Keys with Fluent API'
sidebar_position: 6
description: "explique comment créer les clés primaires d'une entité lorsqu'on utilise la Fluent API"
---

# Keys with Fluent API
*07/01/2020 &sdot; Marc Chevaldonné*  

---

Cet exemple traite des clés primaires associées aux entités.

:::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)
:::

Cet exemple montre le cas particulier de la gestion des clés primaires lors de l'utilisation de la **Fluent API**.  

:::tip 
Vous pourrez trouver une version plus ou moins équivalente avec les *conventions d'écriture* ici : [**2.4. Keys with conventions**](../Entity-Framework/Model/EF_CF_KeysConvention).  
Vous pourrez trouver une version plus ou moins équivalente avec la *data annotations* ici : [**2.5. Keys with data annotations**](../Entity-Framework/Model/EF_CF_KeysDataAnnotations).  
:::

---

## Les clés primaires
Une clé permet de rendre unique chaque instance d'une entité. La plupart des entités n'ont qu'une seule clé qui est alors transformée en *clé primaire* pour les bases de données relationnelles.  
:::note
Une entité peut avoir d'autres clés, on parle d'__alternate keys__. Elles seront présentées dans les exemples sur les relations entre entités.
:::

Si on utilise la *Fluent API*, une propriété pour être transformée en clé doit respecter les contraintes suivantes 
(toutes les modifications se font dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```) :
* aucune contrainte sur le nommage de la propriété ; j'ai par exemple choisi ```UniqueId``` pour ```Nounours```, et ```FrakId``` pour ```Cylon```.
* on définit qu'une propriété est une clé primaire avec la méthode d'extension ```HasKey``` sur la classe ```Entity```.
```csharp 
//définition de la clé primaire de Nounours
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
```  
```csharp
//définition de la clé primaire de Cylon
modelBuilder.Entity<Cylon>().HasKey(c => c.FrakId);
```
* elle peut être générée par la base et dans ce cas, on ajoute à la ```Property``` la méthode d'extension ```ValueGeneratedOnAdd()```.
```csharp
//définition du mode de génération de la clé : génération à l'insertion
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
```
* elle peut ne pas être générée par la base et être gérée par l'utilisateur, dans ce cas on utilise la méthode d'extension ```ValueGeneratedNever()```.
Dans ce dernier cas, c'est à l'utilisateur de gérer ses propres clés et leur unicité dans la base.
```csharp
//définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base)
modelBuilder.Entity<Cylon>().Property(c => c.FrakId).ValueGeneratedNever();
```
* elle peut être de différents types ```int```, ```string```, ```Guid```, ```byte[]```... attention toutefois si vous choisissez de laisser la table générer les valeurs car certains fournisseurs ne savent pas générer tous les types.

## Les clés primaires composites
En *Fluent API* (et seulement en *Fluent API*), on peut définir des clés composites, c'est-à-dire se basant sur plusieurs propriétés.
Pour cela, on utilise également ```HasKey```et on l'associe à un type anonyme, comme par exemple pour la classe ```Ordinateur``` dans cet exemple, dont la clé est un composite des propriétés ```CodeId``` et ```Modele```.
Ainsi, dans la méthode ```OnModelCreating``` de votre classe dérivant de ```DbContext```, on ajoute :
```csharp
//définition d'une clé primaire composite pour Ordinateur
modelBuilder.Entity<Ordinateur>().HasKey(o => new { o.CodeId, o.Modele });
```  
Les clés composites ne peuvent pas être générées par la base.

## Les classes POCO ```Nounours```, ```Cylon``` et ```Ordinateur```
Lorsqu'on utilise la *Fluent API*, ces classes n'ont aucune annotation. La classe ```Ordinateur``` n'a pas de propriété "Id".

## La classe ```DBEntites : DbContext```
La classe dérivant de ```DbContext``` ressemble dès lors dans notre exemple à :
```csharp title='DBEntities.cs'
using Microsoft.EntityFrameworkCore;

namespace ex_042_006_Keys_FluentAPI
{
    class DBEntities : DbContext
    {
        public DbSet<Nounours> NounoursSet { get; set; }
        public DbSet<Cylon> CylonsSet { get; set; }
        public DbSet<Ordinateur> Ordinateurs { get; set; }


        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
              optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_006_Keys_FluentAPI.Nounours.mdf;Trusted_Connection=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //définition de la clé primaire de Nounours
            modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
            //définition du mode de génération de la clé : génération à l'insertion
            modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();

            //définition de la clé primaire de Cylon
            modelBuilder.Entity<Cylon>().HasKey(c => c.FrakId);
            //définition du mode de génération de la clé : génération gérée par l'utilisateur (jamais par la base)
            modelBuilder.Entity<Cylon>().Property(c => c.FrakId).ValueGeneratedNever();

            //définition d'une clé primaire composite pour Ordinateur
            modelBuilder.Entity<Ordinateur>().HasKey(o => new { o.CodeId, o.Modele });
            //une clé composite ne peut pas être générée par la base

            base.OnModelCreating(modelBuilder);
        }
    }
}
```

## La classe ```Program```
Cette classe est le point d'entrée du programme :
* Elle crée des instances de ```Nounours```, de ```Cylon``` et d'```Ordinateur``` et les ajoute en base après avoir nettoyé les tables au préalables.  
Notez que l'utilisateur n'a pas besoin de donner une valeur à ```Nounours.UniqueId``` puisque la base s'en charge, alors qu'il doit donner une valeur à ```Cylon.FrakId``` car la base de ne génère pas les clés.  
Si vous ne donnez pas une valeur à ```Cylon.FrakId```, alors la valeur par défaut est donnée (```0```). Il n'y aura pas de problème si cet identifiant n'a pas été donné, mais dès le deuxième ```Cylon```, vous aurez une exception.  
:::note
La valeur par défaut pour ```int``` est ```0``` ; pour ```Guid```, ```Guid.Empty``` ; pour ```string```, ```null```...
:::
Notez que l'utilisateur doit garantir pour ses instances d'```Ordinateur``` l'unicité des couples ```CodeId```/```Modele```, puisqu'il s'agit d'une clé composite.
```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 { Nom = "Ewok", DateDeNaissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };

Cylon c1 = new Cylon { FrakId = 2, Name = "John Cavil", Generation = 1 };
Cylon c2 = new Cylon { FrakId = 4, Name = "Leoben Conoy", Generation = 2 };
Cylon c3 = new Cylon { FrakId = 6, Name = "D'Anna Biers", Generation = 3 };
Cylon c4 = new Cylon { FrakId = 8, Name = "Simon", Generation = 4 };
Cylon c5 = new Cylon { FrakId = 10, Name = "Aaron Doral", Generation = 5 };
Cylon c6 = new Cylon { FrakId = 12, Name = "Caprica 6", Generation = 6 };
Cylon c7 = new Cylon { FrakId = 14, Name = "Daniel", Generation = 7 };
Cylon c8 = new Cylon { FrakId = 16, Name = "Boomer", Generation = 8 };
Cylon c9 = new Cylon { FrakId = 17, Name = "Athena", Generation = 8 };

Ordinateur o1 = new Ordinateur { Année = 2019, Modele = "MacBook Pro", CodeId = "IUT_1" };
Ordinateur o2 = new Ordinateur { Année = 2017, Modele = "MacBook Pro", CodeId = "IUT_2" };
Ordinateur o3 = new Ordinateur { Année = 2016, Modele = "MacBook Pro", CodeId = "IUT_4" };
Ordinateur o4 = new Ordinateur { Année = 2019, Modele = "Dell Latitude", CodeId = "IUT_1" };
Ordinateur o5 = new Ordinateur { Année = 2012, Modele = "Dell Latitude", CodeId = "IUT_2" };
Ordinateur o6 = new Ordinateur { Année = 2013, Modele = "Dell Latitude", CodeId = "IUT_3" };
```
* Elle affiche les ```Nounours```, les ```Cylon``` et les ```Ordinateur```.
:::note
Notez la génération des identifiants pour la classe ```Nounours``` uniquement : si vous exécutez plusieurs fois l'exemple, les clés des ```Nounours``` changent mais pas celles des ```Cylon```.
:::

## 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_006_Keys_FluentAPI
``` 

:::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_006
```
  * 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_006_Keys_FluentAPI**.

  * 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_006_Keys_FluentAPI.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)  

* Le résultat de l'exécution peut être :
```
database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :
        Nounours 1: Chewbacca (27/05/1977, 1234567 poils)
        Nounours 2: Ewok (25/05/1983, 3456789 poils)
        Nounours 3: Yoda (21/05/1980, 3 poils)
        Cylon 2: John Cavil, Number 1
        Cylon 4: Leoben Conoy, Number 2
        Cylon 6: D'Anna Biers, Number 3
        Cylon 8: Simon, Number 4
        Cylon 10: Aaron Doral, Number 5
        Cylon 12: Caprica 6, Number 6
        Cylon 14: Daniel, Number 7
        Cylon 16: Boomer, Number 8
        Cylon 17: Athena, Number 8
        Computer IUT_1 (Dell Latitude, 2019)
        Computer IUT_1 (MacBook Pro, 2019)
        Computer IUT_2 (Dell Latitude, 2012)
        Computer IUT_2 (MacBook Pro, 2017)
        Computer IUT_3 (Dell Latitude, 2013)
        Computer IUT_4 (MacBook Pro, 2016)
```
:::note
Les identifiants des ```Nounours``` peuvent varier en fonction du nombre d'exécution de l'exemple depuis la création de la base de données, mais pas ceux des ```Cylon``` puisqu'ils sont gérés par l'utilisateur.
:::