|
|
@ -1 +1,298 @@
|
|
|
|
|
|
|
|
# ex_042_003_EF_CF_Fluent_API
|
|
|
|
|
|
|
|
*03/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 la **Fluent API** est utilisée pour transformer une entité en table.
|
|
|
|
|
|
|
|
Il montre notamment :
|
|
|
|
|
|
|
|
* comment choisir le nom de la table
|
|
|
|
|
|
|
|
* comment ignorer une propriété de l'entité
|
|
|
|
|
|
|
|
* comment le nom et le type d'une colonne de la table peuvent être modifiés
|
|
|
|
|
|
|
|
* comment un identifiant unique est choisi et généré.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cette méthode est préférable dans les deux cas principaux suivants (et en particulier le 1er) :
|
|
|
|
|
|
|
|
* nous n'avons pas accès au code source de la classe Nounours ou bien nous n'avons pas le droit de le modifier, et les conventions d'écriture ne
|
|
|
|
|
|
|
|
correspondent pas à ce que nous souhaitons réaliser.
|
|
|
|
|
|
|
|
* nous ne souhaitons pas "polluer" la classe POCO avec des annotations de données.
|
|
|
|
|
|
|
|
L'inconvénient majeur et évident de cette méthode est que la lecture et la maintenance sont plus compliquées.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_001_EF_CF_conventions** : avec les *conventions d'écriture*
|
|
|
|
|
|
|
|
* **ex_042_002_EF_CF_data_annotations** : avec les *data annotations*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Comment est construit cet exemple ?
|
|
|
|
|
|
|
|
* Le projet est de type .NET Core
|
|
|
|
|
|
|
|
* Il contient deux classes :
|
|
|
|
|
|
|
|
* ```Nounours```
|
|
|
|
|
|
|
|
* ```NounoursDBEntities```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### La classe ```Nounours```
|
|
|
|
|
|
|
|
* ```Nounours``` est une entité, on parle aussi de classe POCO, i.e. Plain Old CLR Object.
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
public class Nounours
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public Guid UniqueId
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
get; set;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string Nom
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
get;
|
|
|
|
|
|
|
|
set;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public DateTime DateDeNaissance
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
get;
|
|
|
|
|
|
|
|
set;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int NbPoils
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
get;
|
|
|
|
|
|
|
|
set;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* Elle contient 4 propriétés en lecture/écriture : ```UniqueId```, ```Nom```, ```DateDeNaissance``` et ```NbPoils```.
|
|
|
|
|
|
|
|
* **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 pourrait s'appliquer car il n'y a pas de *data annotations* (cf. ex_042_002) ; mais comme la classe ```NounoursDbEntities``` utilise la *Fluent API*, elles seront écrasées.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 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_003_EF_CF_Fluent_API.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 la *Fluent API* et les conventions d'écriture de la classe ```Nounours```.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* La **Fluent API** est utilisée à travers la méthode ```OnModelCreating```.
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired()
|
|
|
|
|
|
|
|
.HasMaxLength(256);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance).HasColumnName("Naissance").HasColumnType("date");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
L'instance de ```ModelBuilder``` permet ensuite d'atteindre les entités avec la méthode générique ```Entity<T>()```où ```T```est l'entité.
|
|
|
|
|
|
|
|
A partir d'une entité, on peut ensuite atteindre une propriété à l'aide de la méthode ```Property(...)``` pouvant prendre en paramètre
|
|
|
|
|
|
|
|
soit le nom de la propriété, soit une expression lambda (recommandé pour éviter les erreurs de compilation).
|
|
|
|
|
|
|
|
Ensuite, à l'aide des méthodes d'extension, on peut définir des contraintes sur les entités et leurs propriétés.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Si on prend chaque ligne de la méthode ```OnModelCreating``` en détail, on peut voir :
|
|
|
|
|
|
|
|
* le choix du nom de la table associée à l'entité ```Nounours```
|
|
|
|
|
|
|
|
*équivalent du ```[Table("TableNounours")]``` avec les annotations de données*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().ToTable("TableNounours");
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut changer le nom et le type d'une colonne avec les méthodes d'extension ```HasColumnName``` et ```HasColumnType``` sur la propriété de l'entité.
|
|
|
|
|
|
|
|
Dans l'exemple ci-dessous, la colonne ne s'appellera pas "DateDeNaissance" (comme le permettraient les conventions d'écriture), mais "Naissance" grâce à la *Fluent API* et son type sera ```date``` au lieu de ```datetime2(7)``` avec les conventions d'écriture.
|
|
|
|
|
|
|
|
*équivalent avec une annotation de ```[Column("Naissance", TypeName="date")]``` devant la propriété ```DateDeNaissance``` de ```Nounours```*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.DateDeNaissance)
|
|
|
|
|
|
|
|
.HasColumnName("Naissance")
|
|
|
|
|
|
|
|
.HasColumnType("date");
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut rendre une propriété obligatoire (les propriétés sont optionnelles par défaut si elles sont *nullable*) avec la méthode d'extension ```IsRequired()```.
|
|
|
|
|
|
|
|
Dans l'exemple ci-dessous, le nom est obligatoire.
|
|
|
|
|
|
|
|
*équivalent de ```[Required]``` devant la propriété ```Nom``` de ```Nounours```*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.Nom).IsRequired();
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut imposer une taille max à une chaîne de caractères avec la méthode d'extension ```HasMaxLength(...)```.
|
|
|
|
|
|
|
|
Dans l'exemple ci-dessous, le nom ne doit pas avoir plus de 256 caractères.
|
|
|
|
|
|
|
|
*équivalent de ```[MaxLength(256)]``` devant la propriété ```Nom``` de ```Nounours```*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.Nom).HasMaxLength(256);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut chaîner les méthodes d'extension.
|
|
|
|
|
|
|
|
Dans l'exemple ci-dessous, le nom est obligatoire et ne doit pas avoir plus de 256 caractères.
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.Nom)
|
|
|
|
|
|
|
|
.IsRequired()
|
|
|
|
|
|
|
|
.HasMaxLength(256);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut préciser qu'on ne veut pas qu'une propriété soit transformée en colonne de la table avec la méthode d'extension ```Ignore()```.
|
|
|
|
|
|
|
|
Par exemple, ici, le nombre de poils ne sera pas mappé en colonne.
|
|
|
|
|
|
|
|
*équivalent de ```[NotMapped]``` devant la propriété ```NbPoils``` de ```Nounours```*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Ignore(n => n.NbPoils);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* on peut préciser qu'une propriété doit être utilisée en tant que clé primaire avec la méthode d'extension ```HasKey``` prenant en paramètre le nom de la propriété à prendre en compte.
|
|
|
|
|
|
|
|
*équivalent de ```[Key]``` devant la propriété à utiliser en clé primaire*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
On peut ensuite préciser qu'on veut que cette clé soit générée par la base lors de l'insertion à l'aide de la méthode d'extension ```ValueGeneratedOnAdd()```.
|
|
|
|
|
|
|
|
*équivalent de ```[DatabaseGenerated(DatabaseGeneratedOption.Identity)]``` devant la propriété à utiliser en clé primaire*
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
Notez que la clé n'est donc, contrairement aux conventions d'écriture, pas nécessairement un ```int```. Elle peut-être un ```Guid```, un ```string```, etc. Son nom peut-être autre chose que "ID".
|
|
|
|
|
|
|
|
*Note : on peut aussi faire des clés composées, comme je le montrerai dans un autre exemple.*
|
|
|
|
|
|
|
|
Dans l'exemple ci-dessous, un ```Nounours```possédera une clé unique de type ```Guid``` générée par la base et placée dans la colonne "UniqueId".
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().HasKey(n => n.UniqueId);
|
|
|
|
|
|
|
|
modelBuilder.Entity<Nounours>().Property(n => n.UniqueId).ValueGeneratedOnAdd();
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**En résumé**, avec la *Fluent API* :
|
|
|
|
|
|
|
|
* on peut changer le nom de la table
|
|
|
|
|
|
|
|
* on peut changer le nom et le type d'une colonne
|
|
|
|
|
|
|
|
* on peut rendre une propriété obligatoire ou optionnelle
|
|
|
|
|
|
|
|
* on peut empêcher le *mapping* d'une propriété
|
|
|
|
|
|
|
|
* on peut imposer une taille max pour les chaînes de caractères
|
|
|
|
|
|
|
|
* on peut transformer une propriété en clé primaire et demander à la base de la générée 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_003_EF_CF_Fluent_API
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
*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_003
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
* 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_003_EF_CF_Fluent_API**.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* 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_003_EF_CF_Fluent_API.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 :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|UniqueId |Nom|Naissance
|
|
|
|
|
|
|
|
|---|---|---
|
|
|
|
|
|
|
|
|30817fff-8736-457d-db18-08d7908d7986|Chewbacca|27/05/1977
|
|
|
|
|
|
|
|
|aa4469c4-a6c8-4077-db19-08d7908d7986|Yoda|21/05/1980
|
|
|
|
|
|
|
|
|69cb5892-6750-4629-db1a-08d7908d7986|Ewok|25/05/1983
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*Notes: les identifiants seront bien sûr différents.*
|
|
|
|
|
|
|
|
*Notez l'absence de la colonne "NbPoils"*
|
|
|
|
|
|
|
|
*Notez le nom de la colonne "Naissance" et le formatage de la date*
|