A chaque version, Entity framework core (EF core), de dotnet core, s’améliore, devient de plus en plus performant. Dans ce tutorial, nous allons voir comment mettre en place, pour notre jeu autour d’Harry Potter, un lien entre une classe Wizard et une Baguette.
Nous partirons d’une approche code first : c’est le code qui guide la mise à jour de la base de données.
Enfin, nous utiliserons mysql, plutôt que Sql Server..
Ajout d’une classe Wizard avec un Id (par défaut c’est la clef en dotnet ef core)
public class Wizard
{
public int Id { get; set; }
}
Création du context
namespace ConnectWithEfCore.Models.Data
{
public class DefaultDbContext : DbContext
{
}
}
Ajout de la référence
Ajout d’un accès à une liste d’objets
public class DefaultDbContext : DbContext
{
public DbSet<Wizard> Wizards { get; set; }
}
Ajout dans le moteur d’injections de dépendances
builder.Services.AddDbContext<DefaultDbContext>(options =>
{
});
Précision du provider (mysql, sql server)
Ajout de la référence
Configuration du provider
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<DefaultDbContext>(options =>
{
options.UseMySQL("");
});
Bien faire attention au using.
Ajouter une clef de config
Récupération pour notre provider
builder.Services.AddDbContext<DefaultDbContext>(options =>
{
options.UseMySQL(builder.Configuration.GetConnectionString("HarryPotter"));
});
Lister les wizards
Récupération du context par DI
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly DefaultDbContext context;
public HomeController(ILogger<HomeController> logger, DefaultDbContext context)
{
_logger = logger;
this.context = context;
}
Listing des items
public IActionResult Index()
{
var list = this.context.Wizards.ToList();
return View(list);
}
Ajout du bon constructeur pour le context
namespace ConnectWithEfCore.Models.Data
{
public class DefaultDbContext : DbContext
{
public DefaultDbContext(DbContextOptions options) : base(options)
{
}
protected DefaultDbContext()
{
}
public DbSet<Wizard> Wizards { get; set; }
}
}
La table ne s’appelle pas pareil en base
Le nom du DbSet est de base prévu comme le nom de la table.
On peut changer ça.
Ajout d’un EntityTypeConfiguration dédié pour configurer ça.
public class WizardEntityTypeConfiguration : IEntityTypeConfiguration<Wizard>
{
public void Configure(EntityTypeBuilder<Wizard> builder)
{
builder.ToTable("Wizard");
}
}
Puis ajout dans notre créateur de context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new WizardEntityTypeConfiguration());
}
Il manque la table en base
Vérification que dotnet-ef ou dotnet ef est bien présent
Installation de dotnet ef
dotnet tool install –global dotnet-ef
Création d’une migration
dotnet-ef migrations add AddWizard
Il manque une référence à Microsoft.EntityFrameworkCore.Design
On l’installe
On relance, on tombe sur une nouvelle erreur :
Unable to resolve service for type ‘Microsoft.EntityFrameworkCore.Storage.TypeMappingSourceDependencies’ while attempting to activate ‘MySql.EntityFrameworkCore.Storage.Internal.MySQLTypeMappingSource’.
Ajout de la classe
public class MysqlEntityFrameworkDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
{
serviceCollection.AddEntityFrameworkMySQL();
new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection)
.TryAddCoreServices();
}
}
On relance et obtient le dossier et la classe de migration
Lancement de la migration vers la base de données
dotnet-ef database update
La base est mise à jour
Notons l’apparition de la table _efmigrationhistory
et une ligne dans cette table
Ajout d’une Baguette pour chaque Wizard
Création de la classe
public class Wand
{
public int Id { get; set; }
public string Name { get; set; }
}
Lien avec la classe Wizard
public class Wizard
{
public int Id { get; set; }
public Wand FavoriteWand { get; set; }
}
Ajout de la configuration pour le context / base
public class WandEntityTypeConfiguration : IEntityTypeConfiguration<Wand>
{
public void Configure(EntityTypeBuilder<Wand> builder)
{
builder.ToTable("Wand");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new WandEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new WizardEntityTypeConfiguration());
}
Génération d’une nouvelle migration
dotnet-ef migrations add AddWand
Mise à jour de la base de données
..\DiscoverAndMasterizeEfCore\ConnectWithEfCore>dotnet-ef database update
Build started...
Build succeeded.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.12 initialized 'DefaultDbContext' using provider 'MySql.EntityFrameworkCore:6.0.7+MySQL8.0.31' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='dotnet-efcore' AND TABLE_NAME='__EFMigrationsHistory';
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='dotnet-efcore' AND TABLE_NAME='__EFMigrationsHistory';
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `MigrationId`, `ProductVersion`
FROM `__EFMigrationsHistory`
ORDER BY `MigrationId`;
info: Microsoft.EntityFrameworkCore.Migrations[20402]
Applying migration '20230104230412_AddWand'.
Applying migration '20230104230412_AddWand'.
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('20230104230412_AddWand', '6.0.12');
Done.
Notons ici
- La mise à jour de la table d’historique des migrations
- L’ajout de la Table Wand
- L’ajout de la colonne dans la table Wizard
Si votre classe de migration est vide : pensez à compiler le projet, depuis VS.
Dans la classe de migration, il a automatiquement créé une colonne <NomPropriété>Id.
Après mise à jour de la base, il a ajouté la colonne dans la table wizard.
Possibilité de customiser
A noter, vous pouvez aussi créer une relation via le code entre les deux classes
Configure One-to-Many Relationships using Fluent API in Entity Framework Core
Possibilité de créer des bundle de migration depuis dotnet 6
https://learn.microsoft.com/fr-fr/ef/core/what-is-new/ef-core-6.0/whatsnew
https://devblogs.microsoft.com/dotnet/introducing-devops-friendly-ef-core-migration-bundles/
Ajout de la possibilité d’une liste de baguettes pas Wizard
Mise à jour des deux classes
public class Wand
{
public int Id { get; set; }
public string Name { get; set; }
public List<Wizard> Wizards { get; set; }
}
public class Wizard
{
public int Id { get; set; }
public Wand FavoriteWand { get; set; }
public List<Wand> Wands { get; set; }
}
Build et ajout d’une nouvelle migration
dotnet-ef migrations add AddWands
On obtient une erreur
Unable to determine the relationship represented by navigation ‘Wand.Wizards’ of type ‘List<Wizard>’. Either manually configure the relationship, or ignore this property using the ‘[NotMapped]’ attribute or by using ‘EntityTypeBuilder.Ignore’ in ‘OnModelCreating’.
Si on supprime la propriété : FavoriteWand, tout fonctionne.
Il nous avertit qu’on peut, après lancement de la mise à jour, perdre des données.
Note: il a prévu une table qui fait le lien entre les deux tables Wizard et Wand.
migrationBuilder.CreateTable(
name: "WandWizard",
columns: table => new
{
WandsId = table.Column<int>(type: "int", nullable: false),
WizardsId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_WandWizard", x => new { x.WandsId, x.WizardsId });
table.ForeignKey(
name: "FK_WandWizard_Wand_WandsId",
column: x => x.WandsId,
principalTable: "Wand",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_WandWizard_Wizard_WizardsId",
column: x => x.WizardsId,
principalTable: "Wizard",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
S’il n’y arrive pas, ou si des champs supplémentaires sont à ajouter pour l’association :
Wizard + Wand (par exemple : favorite) :
- on va devoir créer une classe qui représente une élément lien les deux classes.
le paramétrer dans les entitype configuration : Configure Many-to-Many Relationships in Entity Framework Core