begun ex_042_004

EFCore3_Reforged
Marc CHEVALDONNE 5 years ago
parent ffc8759b77
commit 57f501c0f1

@ -556,6 +556,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_002_EF_CF_data_annot
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ex_042_003_EF_CF_Fluent_API", "p08_BDD_EntityFramework\ex_042_003_EF_CF_Fluent_API\ex_042_003_EF_CF_Fluent_API.csproj", "{9D444878-F974-4883-9023-1A46E80ADFF1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02. Keys and value generation", "02. Keys and value generation", "{5B333C02-67B7-4A4C-AA58-2710C183292B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ex_042_004_ValueGeneration_conventions", "p08_BDD_EntityFramework\ex_042_004_ValueGeneration_conventions\ex_042_004_ValueGeneration_conventions.csproj", "{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -4612,6 +4616,26 @@ Global
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x64.Build.0 = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x86.ActiveCfg = Release|Any CPU
{9D444878-F974-4883-9023-1A46E80ADFF1}.Release|x86.Build.0 = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|ARM.ActiveCfg = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|ARM.Build.0 = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|ARM64.Build.0 = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|x64.ActiveCfg = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|x64.Build.0 = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|x86.ActiveCfg = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Debug|x86.Build.0 = Debug|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|Any CPU.Build.0 = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|ARM.ActiveCfg = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|ARM.Build.0 = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|ARM64.ActiveCfg = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|ARM64.Build.0 = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x64.ActiveCfg = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x64.Build.0 = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x86.ActiveCfg = Release|Any CPU
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -4875,6 +4899,8 @@ Global
{1566457E-6D28-412C-86A3-95E8821B2DDD} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{568486B5-85E4-4F86-B86C-A373B5F471FD} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{9D444878-F974-4883-9023-1A46E80ADFF1} = {FC04E822-7560-4C80-9E59-C03DB40E9F17}
{5B333C02-67B7-4A4C-AA58-2710C183292B} = {55E00151-58A6-4E7D-9457-0BB8213B82F5}
{0877A4DE-50F4-4A9E-8BA1-D55A37DE87E3} = {5B333C02-67B7-4A4C-AA58-2710C183292B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8D31C3AE-36FF-4667-A2A7-0E670245A59E}

@ -0,0 +1,81 @@
using System;
namespace ex_042_004_ValueGeneration_conventions
{
public class Nounours
{
public int ID
{
get; set;
}
public string Nom
{
get;
set;
}
public DateTime DateDeNaissance
{
get;
set;
}
public int NbPoils
{
get;
set;
}
/// <summary>
/// returns a hash code in order to use this class in hash table
/// </summary>
/// <returns>hash code</returns>
public override int GetHashCode()
{
return Nom.GetHashCode();
}
/// <summary>
/// checks if the "right" object is equal to this Nounours or not
/// </summary>
/// <param name="right">the other object to be compared with this Nounours</param>
/// <returns>true if equals, false if not</returns>
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);
}
/// <summary>
/// checks if this Nounours is equal to the other Nounours
/// </summary>
/// <param name="other">the other Nounours to be compared with</param>
/// <returns>true if equals</returns>
public bool Equals(Nounours other)
{
return (this.Nom.Equals(other.Nom) && this.DateDeNaissance == other.DateDeNaissance);
}
public override string ToString()
{
return $"{ID}: {Nom} ({DateDeNaissance:dd/MM/yyyy}, {NbPoils} poils)";
}
}
}

@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
namespace ex_042_004_ValueGeneration_conventions
{
class NounoursDBEntities : DbContext
{
public virtual DbSet<Nounours> NounoursSet { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_004_ValueGeneration_conventions.Nounours.mdf;Trusted_Connection=True;");
}
}
}

@ -0,0 +1,57 @@
using Microsoft.Data.SqlClient;
using System;
using System.Linq;
using static System.Console;
namespace ex_042_004_ValueGeneration_conventions
{
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 };
try
{
using (NounoursDBEntities db = new NounoursDBEntities())
{
//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("Base après nettoyage et ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
//essai d'ajout d'un Nounours existant
chewie.Nom = "Chewie";
db.NounoursSet.Add(chewie);
db.SaveChanges();
}
}
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();
}
}
}

@ -0,0 +1,171 @@
# ex_042_004_ValueGeneration_conventions
*04/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.
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_DataAnnotations**.
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```
```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 };
```
* 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.
Notez l'accès à la table de ```Nounours``` via ```db.NounoursSet```.
Notez également que tant que ```SaveChanges``` n'est pas appelée, les suppressions ne sont pas effectives dans la base, seulement en local dans le programme.
_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
if (db.NounoursSet.Count() > 0)
{
WriteLine("La base n'est pas vide !");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
WriteLine("début du nettoyage...");
foreach (var n in db.NounoursSet.ToArray())
{
WriteLine($"Suppression de {n}");
db.NounoursSet.Remove(n);
}
WriteLine("Base avant sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
db.SaveChanges();
WriteLine("Base après sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
}
```
* Elle ajoute ensuite les ```Nounours```et sauvegarde les changements pour que ceux-ci soit effectivement ajoutés à la base.
```csharp
db.NounoursSet.AddRange(new Nounours[] { chewie, yoda, ewok });
db.SaveChanges();
WriteLine("Base après ajout des 3 nounours et sauvegarde des changements :");
foreach (var n in db.NounoursSet)
{
WriteLine($"\t{n}");
}
```
## 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_001_EF_CF_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_001
```
* 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_001_EF_CF_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_001_EF_CF_conventions.NounoursSet.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|Ewok|25/05/1983 00:00:00|3456789
*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,12 @@
<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>

@ -11,7 +11,9 @@
* ? configuring a dbcontext ?
* ? nullable reference types => navigating (include, theninclude)
##### creating a model (42)
* entity types, entity properties, keys, generated values, backing fields?
* 001, 002, 003 entity types, entity properties
* keys, generated values
* ? backing fields?
* initialization strategy, seeding data
* shadow properties
* ###### relationships

Loading…
Cancel
Save