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/ReadMe.md

268 lines
14 KiB

# Entity Framework Core 3.0
*06/01/2020 &sdot; Marc Chevaldonn<6E>*
---
Entity Framework (EF) Core est un ORM (Object-Relational Mapper) qui permet aux d<>veloppeurs .NET de g<>rer de mani<6E>re simple, l<>g<EFBFBD>re et extensible, des bases de donn<6E>es.
EF permet de g<>rer de nombreux *providers* (SQL Server, SQLite, Cosmos, ...) de mani<6E>re transparente.
EF vous permet <20>galement de mettre <20> jour vos bases de donn<6E>es et d'ex<65>cuter des requ<71>tes sans avoir <20> <20>crire la moindre requ<71>te SQL. Vous pouvez passer par LINQ to SQL qui apportera plus de lisibilit<69> et permettra au compilateur de vous aider <20> d<>tecter vos erreurs.
---
*Note:*
Diff<EFBFBD>rentes solutions existent avec EF pour g<>rer une base de donn<6E>es dont le mod<6F>le existe par exemple. Dans ces exemples, je ne traiterai que la partie *Code First*, c'est-<2D>-dire le cas o<> le mod<6F>le est cr<63><72> <20> partir de vos classes.
---
## Plan
Les exemples sont organis<69>s selon le plan suivant:
1. *Fundamentals* :
Dans cette partie, je donnerai quelques notions pour se connecter <20> une base <20> l'aide de cha<68>ne de connection (*connection strings*), comment utiliser des *providers de tests...*.
Il s'agira en cons<6E>quence d'exemples simples manquants d'explications sur certains points, car ils seront pr<70>sent<6E>s plus tard.
* **ex_041_001 : Connection Strings** : montre comment utiliser une cha<68>ne de connexion SQL Server ou SQLite.
* **ex_041_004 : Testing in memory** : pr<70>sente comment utiliser des fournisseurs en m<>moire pour <20>viter la surchage de la cr<63>ation d'une base de donn<6E>es en particulier dans le cas de tests unitaires. Cet exemple est compos<6F> de 4 projets.
2. *Model* :
Ce chapitre s'attardera sur le lien entre le mod<6F>le et la base de donn<6E>es. En effet, avec EF, l'acc<63>s aux donn<6E>es se fait via le mod<6F>le, c'est-<2D>-dire l'ensemble de vos classes (qui seront reli<6C>es <20> des tables cr<63><72>es plus ou moins automatiquement)
ainsi qu'un contexte (```DbContext```) qui repr<70>sentera une session de connexion avec votre (ou vos) base(s) de donn<6E>es.
Je pr<70>senterai en cons<6E>quence tout d'abord comment <20>crire des classes pour votre mod<6F>le, puis comment <20>crire les diff<66>rentes relations classiques (aggr<67>gation, *one to one*, *one to many*, *many to many*, mais aussi les dictionnaires), comment g<>rer les h<>ritages entre classes du mod<6F>le dans la base de donn<6E>es, etc.
* **ex_042_001 : conventions d'<27>criture** : explique quelles sont les conventions d'<27>criture utilis<69>es pour la transformation d'une entit<69> en table.
* **ex_042_002 : data annotations** : explique comment utiliser les *data annotations* pour personnaliser la transformation d'une entit<69> en table.
* **ex_042_003 : Fluent API** : explique comment utiliser la *Fluent API* pour personnaliser la transformation d'une entit<69> en table.
* **ex_042_004 : Keys with conventions** : explique comment cr<63>er les cl<63>s primaires d'une entit<69> lorsqu'on utilise les conventions d'<27>criture.
* **ex_042_005 : Keys with data annotations** : explique comment cr<63>er les cl<63>s primaires d'une entit<69> lorsqu'on utilise les *data annotations*.
3. *Schemas and migrations* :
Le but de ce chapitre sera de vous montrer comment garder votre mod<6F>le et votre base de donn<6E>es synchronis<69>s.
4. *Querying (LINQ to SQL) and saving data* :
*Language INtegrated Query* (LINQ) est un outil de requ<71>te sur collections et sa version LINQ to SQL vous permet de passer tr<74>s facilement <20> un syst<73>me de requ<71>tes sur les bases de donn<6E>es.
Les requ<71>tes LINQ sont automatiquement traduites en requ<71>tes SQL, vous <20>vitant ainsi d'avoir <20> <20>crire vos requ<71>tes vous-m<>mes. Elles sont d<>s lors beaucoup plus lisibles et faciles <20> <20>crire.
Ce chapitre pr<70>sente comment charger des donn<6E>es, r<>aliser du filtrage, de mani<6E>re synchrone ou asynchrone, etc.
Il montre bien s<>r <20>galement comment r<>aliser le sym<79>trique : mettre <20> jour, supprimer ou ajouter de nouvelles donn<6E>es dans la base.
5. *Database providers* :
EF vous permet de g<>rer votre base de donn<6E>es ind<6E>pendamment du *provider*. Ce chapitre montrera donc comment utiliser diff<66>rents providers parmi lesquels Microsoft SQL Server, SQLite ou encore InMemory dont le but est de permettre de tester la base en m<>moire, sans passer par un *provider*.
---
## Quelle version utiliser ?
Ces exemples sont <20>crits pour .NET Core 3.0, mais vous pouvez utiliser EF Core avec diff<66>rents types projets. Voici les recommendations actuelles de Microsoft quant <20> l'utilisation des version d'EF.
|**EF Core** |**1.x** |**2.x** |**3.x**
|----------------|--------|-----------|---------------
|.NET Standard |1.3 |2.0 |2.1
|.NET Core |1.0 |2.0 |3.0
|.NET Framework |4.5.1 |4.7.2 |(not supported)
|Mono |4.6 |5.4 |6.4
|Xamarin.iOS |10.0 |10.14 |12.16
|Xamarin.Android |7.0 |8.0 |10.0
|UWP |10.0 |10.0.16299 |to be defined
|Unity |2018.1 |2018.1 |to be defined
*Comment lire ce tableau ?*
Si vous voulez utiliser EF Core 3.0 avec une biblioth<74>que de classes <20>crites en .NET Standard, celle-ci doit utiliser au moins .NET Standard 2.1.
Si vous voulez utiliser EF Core 3.0 avec un projet Xamarin.iOS, celui-ci doit <20>tre au moins en version 12.16.
Si vous voulez utiliser EF Core dans une application UWP, vous ne pouvez pour le moment utiliser que EF Core 1.x ou 2.x.
---
*Note :*
Je n'ai pas l'intention de mettre <20> jour les exemples pour Entity Framework 6 ou pour .NET Framework, puisque la version 5 du framework va unifier .NET Framework et .NET Core. En cons<6E>quence, EF Core sera la nouvelle "norme".
---
## Comment commencer ?
Un petit tutoriel rapide pour savoir comment cr<63>er un projet...
##### Pr<50>requis
Il vous faut au moins la **[version 3.0 du SDK de .NET Core](https://dotnet.microsoft.com/download)**, mais celle-ci est certainement d<>j<EFBFBD> install<6C>e si vous avez install<6C> Visual Studio 2019 16.3 ou plus.
##### Cr<43>ez un nouveau projet
Vous pouvez ensuite cr<63>er un nouveau projet .NET Core 3.x, pour cela :
* lancez Visual Studio
* cr<63>ez un nouveau projet de type **Console App (.NET Core)** en C#
##### Installez EntityFramework Core
Pour ce tutoriel, nous pouvons utiliser SqlServer comme *provider*.
* Pour cela, cliquez droit sur le projet, puis s<>lectionnez *G<>rer les packages NuGet...*.
* Sous l'onglet *Parcourir*, dans la barre de recherche, rentrez *Microsoft.EntityFrameworkCore*
* S<>lectionnez le premier nuget dans sa version la plus r<>cente et lancez l'installation.
* R<>p<EFBFBD>tez les deux derni<6E>res op<6F>rations pour les packages :
* *Microsoft.EntityFrameworkCore.Design*
* *Microsoft.EntityFrameworkCore.SqlServer*
* *Microsoft.EntityFrameworkCore.SqlServer.Design*
##### Cr<43>ez un mod<6F>le
* Ajoutez une nouvelle classe ```Nounours``` au projet.
* Ajoutez le code suivant <20> cette classe :
```csharp
using System;
namespace tutoRapideEFCore
{
class Nounours
{
public int Id { get; set; }
public string Nom { get; set; }
public DateTime Naissance { get; set; }
public int NbPoils { get; set; }
}
}
```
* Ajoutez une nouvelle classe ```NounoursContext``` qui servira de contexte <20> notre mod<6F>le.
* Ajoutez le code suivante <20> cette classe :
```csharp
using Microsoft.EntityFrameworkCore;
namespace tutoRapideEFCore
{
class NounoursContext : DbContext
{
public DbSet<Nounours> Nounours { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=myFirstDatabase.mdf;Trusted_Connection=True;");
}
}
```
##### Cr<43>ez la base de donn<6E>es
* 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 (*eg* si votre projet s'apelle **tutoRapideEFCore**) :
```
cd tutoRapideEFCore
```
* tapez ensuite les commandes suivantes :
```
dotnet ef migrations add myFirstMigration
dotnet ef database update
```
*Note*:
Si vous n'avez pas install<6C> correctement EntityFrameworkCore, il vous faudra peut-<2D>tre utiliser <20>galement :
```dotnet tool install --global dotnet-ef```
##### Utilisez votre base de donn<6E>es via Entity Framework Core
* Editez *Program.cs* et ajoutez le code suivant :
```csharp
static void Main(string[] args)
{
Nounours chewie = new Nounours { Nom = "Chewbacca", Naissance = new DateTime(1977, 5, 27), NbPoils = 1234567 };
Nounours yoda = new Nounours { Nom = "Yoda", Naissance = new DateTime(1980, 5, 21), NbPoils = 3 };
Nounours ewok = new Nounours { Nom = "Ewok", Naissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };
Nounours c3po = new Nounours { Nom = "C3PO", Naissance = new DateTime(1977, 5, 27), NbPoils = 0 };
using (var context = new NounoursContext())
{
// Cr<43>e des nounours et les ins<6E>re dans la base
Console.WriteLine("Creates and inserts new Nounours");
context.Add(chewie);
context.Add(yoda);
context.Add(ewok);
context.Add(c3po);
context.SaveChanges();
}
}
```
Maintenant, lorsque vous lancerez l'application, la base de donn<6E>es contiendra 3 nounours. La base cr<63>e automatiquement des identifiants pour chaque Nounours.
* Editez *Program.cs* pour rajouter les lignes suivantes <20> la fin de la m<>thode ```Main``` :
```csharp
// Lit le premier nounours de la base dont le nom commence par 'e'
Console.WriteLine("Creates and executes a query retrieving the first Nounours of the database whose name starts with an \"e\":");
var eNounours = context.Nounours
.Where(n => n.Nom.StartsWith("e"))
.First();
Console.WriteLine($"{eNounours.Nom} (born in {eNounours.Naissance.Year})");
```
Cette requ<71>te LINQ vous permet de lire le premier nounours de la base de nounours tri<72>s par ordre alphab<61>tique de noms.
Ceci n<>cessite de rajouter au-d<>but du fichier *Program.cs* la ligne suivante :
```csharp
using System.Linq;
```
* Editez *Program.cs* pour rajouter les lignes suivantes <20> la fin de la m<>thode ```Main``` :
```csharp
// Met <20> jour le nom du second nounours de la base
Console.WriteLine("Updates the name of the second nounours");
eNounours.Nom = "Wicket";
context.SaveChanges();
```
Cette partie du code montre comment mettre <20> jour un <20>l<EFBFBD>ment de la base de donn<6E>es.
* Editez *Program.cs* pour rajouter les lignes suivantes <20> la fin de la m<>thode ```Main``` :
```csharp
// r<>cup<75>re le nounours qui n'en est pas un et le supprime de la base
Console.WriteLine("Deletes one item from de database");
var droid = context.Nounours
.SingleOrDefault(n => n.Nom.Equals("C3PO"));
context.Remove(droid);
context.SaveChanges();
```
Cette partie du code montre comment supprimer un <20>l<EFBFBD>ment de la base de donn<6E>es.
Voici un r<>capitulatif du fichier *Program.cs* :
```csharp
using System;
using System.Linq;
namespace tutoRapideEFCore
{
class Program
{
static void Main(string[] args)
{
Nounours chewie = new Nounours { Nom = "Chewbacca", Naissance = new DateTime(1977, 5, 27), NbPoils = 1234567 };
Nounours yoda = new Nounours { Nom = "Yoda", Naissance = new DateTime(1980, 5, 21), NbPoils = 3 };
Nounours ewok = new Nounours { Nom = "Ewok", Naissance = new DateTime(1983, 5, 25), NbPoils = 3456789 };
Nounours c3po = new Nounours { Nom = "C3PO", Naissance = new DateTime(1977, 5, 27), NbPoils = 0 };
using (var context = new NounoursContext())
{
// Cr<43>e des nounours et les ins<6E>re dans la base
Console.WriteLine("Creates and inserts new Nounours");
context.Add(chewie);
context.Add(yoda);
context.Add(ewok);
context.Add(c3po);
context.SaveChanges();
// Lit le premier nounours de la base dont le nom commence par 'e'
Console.WriteLine("Creates and executes a query retrieving the first Nounours of the database whose name starts with an \"e\":");
var eNounours = context.Nounours
.Where(n => n.Nom.StartsWith("e"))
.First();
Console.WriteLine($"{eNounours.Nom} (born in {eNounours.Naissance.Year})");
// Met <20> jour le nom du second nounours de la base
Console.WriteLine("Updates the name of the second nounours");
eNounours.Nom = "Wicket";
context.SaveChanges();
// r<>cup<75>re le nounours qui n'en est pas un et le supprime de la base
Console.WriteLine("Deletes one item from de database");
var droid = context.Nounours
.SingleOrDefault(n => n.Nom.Equals("C3PO"));
context.Remove(droid);
context.SaveChanges();
}
}
}
}
```
* Ex<45>cutez votre application pour v<>rifier son bon fonctionnement.
##### V<>rifiez le contenu de la base de donn<6E>es avec l'Explorateur d'objets 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<6E>es*
* puis celle portant le nom de votre migration, dans mon cas : *myFirstDatabase.Nounours.mdf*
* puis *Tables*
* Faites un clic droit sur la table *dbo.Nounours* puis choisissez *Afficher les donn<6E>es*
* Vous devriez maintenant pouvoir voir les donn<6E>es suivantes dans le tableau :
|Id |Nom |Naissance |NbPoils
|---|---|---|---
|1|Chewbacca|27/05/1977 00:00:00|1234567
|2|Yoda|21/05/1980 00:00:00|3
|3|Wicket|25/05/1983 00:00:00|3456789
Vous pouvez constater que l'Ewok a bien <20>t<EFBFBD> renomm<6D> Wicket, et que C3PO a bien <20>t<EFBFBD> supprim<69>.
Notez qu'il est <20>galement possible d'utiliser l'*Explorateur d'objets SQL Server* pour ajouter, modifier ou supprimer des donn<6E>es dans les tables.