Entity framework Core (Ef core) - De A à Z - avec dotnet core

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

Tu souhaites consolider tes bases en C# ?

Notre adresse

1 rue du guesclin
44000 Nantes

Notre téléphone

+33 2 79 65 52 87

Société

DevToBeCurious SARL
84860163900018 - Nantes B 848 601 639