Utilisation d’un projet de migrations distinctes

Vous pouvez stocker des migrations dans un projet différent de celui contenant votre DbContext. Cela est recommandé lorsque le projet d’application est spécifique à la plateforme, tel que WinUI, .NET MAUI, Blazor WebAssembly ou Azure Functions, ou lorsqu’il cible un identificateur d’exécution spécifique (RID). Il peut également être utilisé pour gérer plusieurs ensembles de migrations.

Tip

Vous pouvez afficher l'sample de cet article sur GitHub.

Disposition du projet

L’exemple utilise trois projets :

Projet Responsabilité References
WebApplication1.Data Possède les types d’entités et les types d’entités DbContext Fournisseur EF Core
WebApplication1.Migrations Possède les migrations, l’instantané du modèle et la création de contexte au moment du design Projet de données, fournisseur EF Core et Microsoft.EntityFrameworkCore.Design
WebApplication1 Exécute l’application Projet de données et projet de migrations

L’application a besoin d’une référence au projet de migrations lorsqu’elle découvre ou applique des migrations au moment de l’exécution, par exemple en appelant Migrate. Si les migrations sont appliquées uniquement par un artefact de déploiement et que l’application ne les charge jamais, cette référence n’est pas requise.

Configurer les projets

  1. Créez une bibliothèque de classes pour les migrations et ajoutez une référence au projet contenant le DbContext.

  2. Ajoutez le fournisseur de base de données et Microsoft.EntityFrameworkCore.Design le projet de migration. Marquez le package de conception comme dépendance de développement privé :

    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="...">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      </PackageReference>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="..." />
    </ItemGroup>
    
    <ItemGroup>
      <ProjectReference Include="..\WebApplication1.Data\WebApplication1.Data.csproj" />
    </ItemGroup>
    
  3. Implémentez IDesignTimeDbContextFactory<TContext> dans le projet de migrations. La fabrique permet aux outils de créer le contexte sans exécuter le projet d’application :

    public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
    {
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var connectionString = args.FirstOrDefault()
                ?? @"Server=(localdb)\mssqllocaldb;Database=WebApplication1;Trusted_Connection=True";
    
            var options = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseSqlServer(
                    connectionString,
                    sqlServer => sqlServer.MigrationsAssembly(typeof(ApplicationDbContextFactory).Assembly.GetName().Name))
                .Options;
    
            return new ApplicationDbContext(options);
        }
    }
    

    Gardez la configuration du fournisseur au moment du design et du modèle cohérente avec la configuration du runtime. L’exemple accepte un argument chaîne de connexion facultatif et utilise une connexion de développement locale lorsqu’aucun argument n’est fourni.

  4. Configurez l’assembly de migrations lors de l’inscription du contexte au moment de l’exécution :

    services.AddDbContext<ApplicationDbContext>(
        options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection"),
                x => x.MigrationsAssembly("WebApplication1.Migrations")));
    
  5. Si l’application applique des migrations ou les découvre au moment de l’exécution, ajoutez une référence normale de l’application au projet migrations :

    <ItemGroup>
      <ProjectReference Include="..\WebApplication1.Migrations\WebApplication1.Migrations.csproj" />
    </ItemGroup>
    

    Le projet de données ne doit pas référencer le projet de migrations. Cela créerait une dépendance circulaire, car le projet migrations fait déjà référence au projet de données.

  6. Si des migrations existent déjà, déplacez tous les fichiers de migration et l’instantané de modèle vers le projet de migrations et mettez à jour leurs espaces de noms. Lorsqu’il n’existe aucune migration existante, la fabrique au moment du design permet de créer la migration initiale directement dans le projet de migrations.

Utiliser les outils

Utilisez le projet de migration comme projet cible et projet de démarrage. Le projet cible reçoit les fichiers générés, tandis que le projet de démarrage est généré et exécuté par les outils. Dans cette disposition, l’utilisation du projet de migrations pour les deux empêche les outils d’exécuter le code de démarrage de l’application.

Exécutez ces commandes à partir du répertoire de solution :

dotnet ef migrations add NewMigration \
    --project WebApplication1.Migrations \
    --startup-project WebApplication1.Migrations

Les mêmes options de projet s’appliquent à d’autres commandes :

dotnet ef migrations list \
    --project WebApplication1.Migrations \
    --startup-project WebApplication1.Migrations

dotnet ef migrations script --output artifacts/migrations.sql \
    --project WebApplication1.Migrations \
    --startup-project WebApplication1.Migrations

dotnet ef migrations bundle --output artifacts/efbundle \
    --project WebApplication1.Migrations \
    --startup-project WebApplication1.Migrations

À compter d’EF Core 11, les options de projet répétées peuvent être stockées dans .config/dotnet-ef.json.

Générez le projet de migrations avant d’exécuter des commandes avec --no-build, ou avant qu’un autre processus consomme sa sortie. Une commande normale dotnet ef génère automatiquement les projets cibles et de démarrage.

Applications spécifiques à la plateforme

N’utilisez pas de projet d’application spécifique à la plateforme comme projet de démarrage pour les outils EF. Les projets mobiles, navigateur, bureau, fonction et RID peuvent nécessiter une charge de travail ou un hôte natif qui dotnet ef ne peut pas s’exécuter. À compter d’EF Core 11, les outils avertissent lorsqu’un projet de démarrage spécifique à la plateforme est utilisé.

Utilisez la disposition décrite ci-dessus pour .NET MAUI, WinUI, Blazor WebAssembly, Azure Functions et applications similaires :

  1. Placez le contexte et les types d’entités dans un projet de données partagées.
  2. Placez les migrations et IDesignTimeDbContextFactory<TContext> dans un projet de .NET multiplateforme normal.
  3. Exécutez les outils avec le projet de migration en tant que projet cible et de démarrage.
  4. Référencez le projet de migrations à partir de l’application uniquement si l’application charge ou applique des migrations au moment de l’exécution.

La prise en charge des outils directs pour les projets de plateforme Xamarin et MAUI n'est pas planifiée ; consultez dotnet/efcore#7152. Xamarin applications doivent d’abord être mises à niveau vers .NET MAUI.

Architecture de processus

Le processus exécutant les outils doit être en mesure de charger chaque assembly au moment du design. Un processus de Visual Studio ou de .NET 64 bits ne peut pas charger un assembly de démarrage x86 uniquement, et la même contrainte s'applique à Arm64 et à d'autres architectures. Préférez un projet de migration AnyCPU. Si les dépendances au moment du design nécessitent une architecture spécifique, appelez explicitement un .NET SDK correspondant.

L’architecture du processus au moment de la conception est distincte de la cible de déploiement. Lors de la création d’un bundle, utilisez --target-runtime ou -TargetRuntime générez un artefact pour le RID de déploiement, tel que linux-arm64 ou osx-arm64.