parent
08fadfa394
commit
f2ddc019a7
@ -1,175 +0,0 @@
|
||||
# ex_042_004_ValueGeneration_conventions
|
||||
*04/01/2020 ⋅ 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 **conventions d'écriture**.
|
||||
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**.
|
||||
|
||||
---
|
||||
|
||||
## 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 conventions d'écriture.
|
||||
* 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.
|
||||
```csharp
|
||||
public class Nounours
|
||||
{
|
||||
public int ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NbPoils
|
||||
{
|
||||
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.*
|
@ -1,247 +0,0 @@
|
||||
# ex_042_005_ValueGeneration_data_annotations
|
||||
*05/01/2020 ⋅ 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,195 @@
|
||||
# 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```.
|
||||
```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
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ex_042_006_Keys_FluentAPI
|
||||
{
|
||||
class DBEntities : DbContext
|
||||
{
|
||||
public virtual DbSet<Nounours> NounoursSet { get; set; }
|
||||
public virtual DbSet<Cylon> CylonsSet { get; set; }
|
||||
public virtual 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
|
||||
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*.
|
||||
<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_006_Keys_FluentAPI.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"/>
|
||||
|
||||
* 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.*
|
@ -1,12 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.0</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,284 @@
|
||||
# ex_042_007_ValueGeneration
|
||||
*07/01/2020 ⋅ 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*
|
||||
* il est conseillé d'avoir également lu les exemples sur les clés primaires : *ex_042_004_Keys_conventions*, *ex_042_005_Keys_data_annotations* et *ex_042_006_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
|
||||
[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
|
||||
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);
|
||||
}
|
||||
```
|
||||
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
|
||||
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
|
||||
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 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
|
||||
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
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasDefaultValue(DateTime.UtcNow);
|
||||
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasDefaultValue("");
|
||||
```
|
||||
ajoutées à ```OnModelCreating```.
|
||||
|
||||
```csharp
|
||||
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
|
||||
```
|
||||
*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*.
|
||||
<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_007_ValueGeneration.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.*
|
Loading…
Reference in new issue