You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mchsamples-.net-core/p08_BDD_EntityFramework/ex_042_006_Keys_FluentAPI
Marc CHEVALDONNE a773557095
continuous-integration/drone/push Build is failing Details
update to .NET6
2 years ago
..
Cylon.cs added ex_042_006 et ex_042_007 5 years ago
DBEntities.cs minor fixes and read me 5 years ago
Nounours.cs added ex_042_006 et ex_042_007 5 years ago
Ordinateur.cs added ex_042_006 et ex_042_007 5 years ago
Program.cs added ex_042_006 et ex_042_007 5 years ago
ReadMe.md minor fixes and read me 5 years ago
ex_042_006_Keys_FluentAPI.csproj update to .NET6 2 years ago

ReadMe.md

ex_042_006_Keys_FluentAPI

07/01/2020 ⋅ Marc Chevaldonné


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

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 gestion des clés primaires lors de l'utilisation la Fluent API.
Vous pourrez trouver une version plus ou moins équivalente avec les conventions d'écriture ici : ex_042_004_Keys_conventions.
Vous pourrez trouver une version plus ou moins équivalente avec la data annotations ici : ex_042_005_Keys_data_annotations.


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.
//définition de la clé primaire de Nounours
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
//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().
//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.
//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 HasKeyet 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 :

//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 à :

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

  • 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
  • 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.