parent
52b9f5eab6
commit
785d53798c
@ -0,0 +1,211 @@
|
||||
# ex_042_001_EF_CF_conventions
|
||||
*02/01/2020 ⋅ Marc Chevaldonné*
|
||||
|
||||
---
|
||||
|
||||
Le lien entre une base de données et vos classes à l'aide de **Entity Framework Core** se fait via une classe dérivant de ```DbContext```.
|
||||
Cette classe doit contenir des ```DbSet<T>```. Chaque ```DbSet<T>``` correspond à une table et ```T``` correspond à une de vos classes qu'on appelle *entité*.
|
||||
Le lien entre les tables et les entités est géré plus ou moins automatiquement par le *framework*.
|
||||
**EF Core** permet de lire/écrire les instances d'entité de la base de données ; permet de créer des tables pour les entités via les migrations (pour les bases de données relationnelles) ;
|
||||
les types exposés dans les propriétés d'une entité deviennent automatiquement des entités (mais pas forcément des tables)
|
||||
|
||||
**Entity Framework Core** propose 3 solutions différentes pour relier des entités à des tables de la base de données :
|
||||
* **conventions d'écriture** : c'est la solution la plus simple, qui analyse le code de votre classe pour en déduire de quelle manière la relier à la table.
|
||||
* **data annotations** : elle se base sur des décorateurs (appelés *data annotations*) que vous placez autour de vos propriétés pour indiquer de quelle manière les relier aux colonnes de votre table. Les *data annotations* écrasent les conventions d'écriture.
|
||||
* **Fluent API** : directement dans le code de votre ```DbContext``` (plus précisément, dans la méthode ```OnModelCreating```), vous précisez comment se fait le *mapping* entre votre entité et votre table. La *Fluent API* écrase les conventions d'écriture et les *data annotations*.
|
||||
|
||||
En d'autres termes, si vous n'écrivez rien, **EF Core** utilise les conventions d'écriture ; si vous n'utilisez que des *data annotations*, elles sont prioritaires sur les convetions d'écriture ; si vous utilisez la *Fluent API*, elle est prioritaire sur les deux autres méthodes.
|
||||
Mais on peut faire un mix des différentes méthodes.
|
||||
|
||||
Cet exemple montre de quelle manière les **conventions d'écriture** sont utilisées pour transformer une entité en table.
|
||||
Il montre notamment :
|
||||
* comment le nom de la table est choisi
|
||||
* s'il est possible d'ignorer une propriété de l'entité
|
||||
* comment le nom et le type d'une colonne de la table sont choisis
|
||||
* comment un identifiant unique est choisi et généré.
|
||||
|
||||
Cet exemple est répété d'une manière très similaire en utilisant les autres méthodes de *mapping* entre entité et table :
|
||||
* **ex_042_002_EF_CF_data_annotations** : avec les *data annotations*
|
||||
* **ex_042_003_EF_CF_Fluent_API** : avec la *Fluent API*
|
||||
|
||||
---
|
||||
|
||||
## Comment est construit cet exemple ?
|
||||
* Le projet est de type .NET Core
|
||||
* Il contient deux classes :
|
||||
* ```Nounours```
|
||||
* ```NounoursDBEntities```
|
||||
|
||||
### La classe ```NounoursDBEntities```
|
||||
* Cette classe dérive de ```DbContext```. Elle contient des ```DbSet<T>``` où ```T``` est une entité. Elle contient autant de ```DbSet<T>``` que de tables.
|
||||
```csharp
|
||||
class NounoursDBEntities : DbContext
|
||||
{
|
||||
public virtual DbSet<Nounours> NounoursSet { get; set; }
|
||||
}
|
||||
```
|
||||
ici, on indique donc qu'il y aura une table de ```Nounours```.
|
||||
* **EF Core** utilisera cette classe pour faire la création de la base de données et de ses tables. Pour cela, elle utilise la chaîne de connexion donnée dans la méthode ```OnConfiguring```.
|
||||
```csharp
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=ex_042_001_EF_CF_conventions.Nounours.mdf;Trusted_Connection=True;");
|
||||
}
|
||||
```
|
||||
=> cf **ex_041_001_ConnectionStrings** pour en savoir plus sur les chaîne de connexion
|
||||
* Dans cet exemple, la table de ```Nounours``` est créée en utilisant les conventions d'écriture de la classe ```Nounours```.
|
||||
|
||||
### La classe ```Nounours```
|
||||
* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object.
|
||||
```csharp
|
||||
public class Nounours
|
||||
{
|
||||
public int ID
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public string Nom
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DateTime DateDeNaissance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int NbPoils
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
```
|
||||
* Elle contient 3 propriétés en lecture/écriture : ```Nom```, ```DateDeNaissance``` et ```NbPoils```. Nous parlerons de la 4ème (```ID```) dans un moment.
|
||||
* **Entity Framework Core** va utiliser la classe POCO ```Nounours``` pour créer une table dans la base de données, lorsque le ```DbSet``` va être créé.
|
||||
* L'utilisation des conventions d'écriture d'Entity Framework font que la table qui va être créée aura :
|
||||
* un nom correspondant au nom de la classe POCO : ici "Nounours"
|
||||
* une colonne pour chaque propriété publique : ici, "Nom", "DateDeNaissance" et "NbPoils"
|
||||
* De plus, en rajoutant une propriété de type int et avec le nom "ID", Entity Framework ajoute directement une colonne "ID" et l'utilise comme clé primaire.
|
||||
|
||||
**En résumé** :
|
||||
* le nom de la table est choisi automatiquement (c'est le nom de l'entité)
|
||||
* toutes les propriétés ayant un getter et un setter publiques sont des colonnes de la table
|
||||
* le nom des colonnes est choisi automatiquement (il s'agit du nom des propriétés)
|
||||
* il n'est pas possible d'ignorer une propriété
|
||||
* le type d'une colonne est imposé par un *mapping* automatique entre les types .NET et ceux de la base de données. Par exemple :
|
||||
* un ```DateTime``` est transformé en ```datetime2(7)```
|
||||
* un ```string``` est transformé en ```nvarchar(max)```
|
||||
* si c'est une clé, elle est transformée en ```nvarchar(450)```
|
||||
* ...
|
||||
* si la classe possède une propriété de type ```int``` s'appelant ```ID```, elle est automatiquement utilisée comme clé primaire générée par lz base de données lors de l'insertion
|
||||
|
||||
### 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,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</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,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</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,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</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>
|
Loading…
Reference in new issue