diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Cylon.cs b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Cylon.cs new file mode 100644 index 0000000..910ce4c --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Cylon.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; + +namespace ex_042_005_Keys_data_annotations +{ + public class Cylon + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int FrakId + { + get; set; + } + + public string Name + { + get; set; + } + + public int Generation + { + get; set; + } + + public override string ToString() + { + return $"Cylon {FrakId}: {Name}, Number {Generation}"; + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/DBEntities.cs b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/DBEntities.cs new file mode 100644 index 0000000..683d4c5 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/DBEntities.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; + +namespace ex_042_005_Keys_data_annotations +{ + class DBEntities : DbContext + { + public virtual DbSet NounoursSet { get; set; } + public virtual DbSet CylonsSet { get; set; } + + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_005_Keys_data_annotations.Nounours.mdf;Trusted_Connection=True;"); + } + } +} diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs new file mode 100644 index 0000000..6db9406 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Nounours.cs @@ -0,0 +1,85 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ex_042_005_Keys_data_annotations +{ + public class Nounours + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int UniqueId + { + get; set; + } + + public string Nom + { + get; + set; + } + + public DateTime DateDeNaissance + { + get; + set; + } + + public int NbPoils + { + 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 $"Nounours {UniqueId}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)"; + } + + } +} diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Program.cs b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Program.cs new file mode 100644 index 0000000..fae8656 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/Program.cs @@ -0,0 +1,79 @@ +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using static System.Console; + +namespace ex_042_005_Keys_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 { 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 }; + + try + { + using (DBEntities db = new DBEntities()) + { + //nettoyage de la base de données + if (db.NounoursSet.Count() > 0) + { + foreach (var n in db.NounoursSet) + { + db.NounoursSet.Remove(n); + } + db.SaveChanges(); + } + + if (db.CylonsSet.Count() > 0) + { + foreach (var c in db.CylonsSet) + { + db.CylonsSet.Remove(c); + } + db.SaveChanges(); + } + + //ajout des nounours dans la base de données + db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok }); + db.CylonsSet.AddRange(new Cylon[] { c1, c2, c3, c4, c5, c6, c7, c8, c9 }); + db.SaveChanges(); + } + using (DBEntities db = new DBEntities()) + { + WriteLine("database after cleaning and adding 3 Nounours and 9 Cylons and saving changes :"); + foreach (var n in db.NounoursSet) + { + WriteLine($"\t{n}"); + } + foreach (var c in db.CylonsSet) + { + WriteLine($"\t{c}"); + } + } + } + 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_Keys_data_annotations/ReadMe.md b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md new file mode 100644 index 0000000..1651f1a --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ReadMe.md @@ -0,0 +1,128 @@ +# ex_042_005_Keys_data_annotations_ +*06/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 des **data annotations**. +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 *Fluent API* ici : **ex_042_006_Keys_FluentAPI**. + +--- + +## 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 les *data annotations*, une propriété pour être transformée en clé doit respecter les contraintes suivantes : +* aucune contrainte sur le nommage de la propriété ; j'ai par exemple choisi ```UniqueId``` pour ```Nounours```, et ```FrakId``` pour ```Cylon```. +* elle doit être précédée de l'annotation ```[Key]``` +* elle peut être générée par la base et dans ce cas elle doit être précédée de l'annotation ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]```, ou ne pas être générée par la base et dans ce cas être précédée de l'annotation ```[DatabaseGenerated(DatabaseGeneratedOption.None)]```. +Dans ce dernier cas, c'est à l'utilisateur de gérer ses propres clés et leur unicité dans la base. +* 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. + +## La classe ```Nounours``` +La classe ```Nounours``` utilise les *data annotations* et laisse le soin à la base de générer les clés uniques. +```csharp +[Key] +[DatabaseGenerated(DatabaseGeneratedOption.Identity)] +public int UniqueId +{ + get; set; +} +``` + +## La classe ```Cylon``` +La classe ```Cylon``` utilise les *data annotations* et laisse le soin à l'utilisateur de gérer ses clés uniques. +```csharp +[Key] +[DatabaseGenerated(DatabaseGeneratedOption.None)] +public int FrakId +{ + get; set; +} +``` + +### La classe ```Program``` +Cette classe est le point d'entrée du programme : +* Elle crée des instances de ```Nounours``` et de ```Cylon``` 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```... * +```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 { 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 }; +``` +* Elle affiche les ```Nounours``` et les ```Cylon```. +*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_005_Keys_data_annotations +``` + *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_005 +``` + * 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_005_Keys_data_annotations**. + + * 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_Keys_conventions.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: Yoda (21/05/1980, 3 poils) + Nounours 3: Ewok (25/05/1983, 3456789 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 +``` +*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.* \ No newline at end of file diff --git a/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ex_042_005_Keys_data_annotations.csproj b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ex_042_005_Keys_data_annotations.csproj new file mode 100644 index 0000000..a20ecd6 --- /dev/null +++ b/p08_BDD_EntityFramework/ex_042_005_Keys_data_annotations/ex_042_005_Keys_data_annotations.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + +