Utilizzo di un progetto di migrazioni separate

È possibile archiviare le migrazioni in un progetto diverso da quello contenente .DbContext Questa operazione è consigliata quando il progetto dell'applicazione è specifico della piattaforma, ad esempio WinUI, .NET MAUI, Blazor WebAssembly o Funzioni di Azure o quando è destinato a un identificatore di runtime specifico (RID). Può essere usato anche per mantenere più di un set di migrazioni.

Tip

È possibile visualizzare il sample di questo articolo in GitHub.

Layout del progetto

L'esempio usa tre progetti:

Progetto Responsabilità References
WebApplication1.Data Possiede i DbContext tipi di entità e Provider EF Core
WebApplication1.Migrations Possiede migrazioni, snapshot del modello e creazione del contesto in fase di progettazione Progetto di dati, provider EF Core e Microsoft.EntityFrameworkCore.Design
WebApplication1 Esegue l'applicazione Progetto di progetti di dati e migrazioni

L'applicazione necessita di un riferimento al progetto di migrazioni quando individua o applica migrazioni in fase di esecuzione, ad esempio chiamando Migrate. Se le migrazioni vengono applicate solo da un artefatto di distribuzione e l'applicazione non le carica mai, tale riferimento non è obbligatorio.

Configurare i progetti

  1. Creare una libreria di classi per le migrazioni e aggiungere un riferimento al progetto contenente .DbContext

  2. Aggiungere il provider di database e Microsoft.EntityFrameworkCore.Design al progetto di migrazioni. Contrassegnare il pacchetto di progettazione come dipendenza di sviluppo privato:

    <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. Implementare IDesignTimeDbContextFactory<TContext> nel progetto delle migrazioni. La factory consente agli strumenti di creare il contesto senza eseguire il progetto dell'applicazione:

    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);
        }
    }
    

    Mantenere il provider e la configurazione del modello in fase di progettazione coerenti con la configurazione di runtime. L'esempio accetta un argomento facoltativo stringa di connessione e usa una connessione di sviluppo locale quando non viene fornito alcun argomento.

  4. Configurare l'assembly delle migrazioni durante la registrazione del contesto in fase di esecuzione:

    services.AddDbContext<ApplicationDbContext>(
        options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection"),
                x => x.MigrationsAssembly("WebApplication1.Migrations")));
    
  5. Se l'applicazione applica migrazioni o li individua in fase di esecuzione, aggiungere un riferimento normale dall'applicazione al progetto di migrazioni:

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

    Il progetto di dati non deve fare riferimento al progetto di migrazioni. In questo modo si creerebbe una dipendenza circolare perché il progetto di migrazioni fa già riferimento al progetto di dati.

  6. Se esistono già migrazioni, spostare tutti i file di migrazione e lo snapshot del modello nel progetto delle migrazioni e aggiornare gli spazi dei nomi. Quando non sono presenti migrazioni, la factory in fase di progettazione consente di creare la migrazione iniziale direttamente nel progetto di migrazioni.

Usare gli strumenti

Usare il progetto di migrazioni sia come progetto di destinazione che come progetto di avvio. Il progetto di destinazione riceve i file generati, mentre il progetto di avvio viene compilato ed eseguito dagli strumenti. In questo layout, l'uso del progetto di migrazioni per entrambi impedisce agli strumenti di eseguire il codice di avvio dell'applicazione.

Eseguire questi comandi dalla directory della soluzione:

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

Le stesse opzioni di progetto si applicano ad altri comandi:

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

A partire da EF Core 11, le opzioni di progetto ripetute possono essere archiviate in .config/dotnet-ef.json.

Compilare il progetto di migrazioni prima di eseguire i comandi con --no-buildo prima che un altro processo ne consumi l'output. Un comando normale dotnet ef compila automaticamente i progetti di destinazione e avvio.

Applicazioni specifiche della piattaforma

Non usare un progetto di applicazione specifico della piattaforma come progetto di avvio per gli strumenti di Entity Framework. I progetti specifici per dispositivi mobili, browser, desktop, funzione e RID possono richiedere un carico di lavoro o un host nativo che dotnet ef non può essere eseguito. A partire da EF Core 11, gli strumenti avvisano quando viene usato un progetto di avvio specifico della piattaforma.

Usare il layout descritto in precedenza per .NET MAUI, WinUI, Blazor WebAssembly, Funzioni di Azure e applicazioni simili:

  1. Inserire il contesto e i tipi di entità in un progetto di dati condiviso.
  2. Inserire le migrazioni e IDesignTimeDbContextFactory<TContext> in un normale progetto multipiattaforma .NET.
  3. Eseguire gli strumenti con il progetto delle migrazioni come progetto di destinazione e avvio.
  4. Fare riferimento al progetto delle migrazioni dall'applicazione solo se l'applicazione carica o applica migrazioni in fase di esecuzione.

Il supporto diretto degli strumenti per i progetti di piattaforma Xamarin e MAUI non è pianificato. Vedere dotnet/efcore#7152. Xamarin le applicazioni devono prima essere aggiornate a .NET MAUI.

Architettura dei processi

Il processo che esegue gli strumenti deve essere in grado di caricare ogni assembly in fase di progettazione. Un processo di Visual Studio a 64 bit o .NET non può caricare un assembly di avvio solo x86 e lo stesso vincolo si applica ad Arm64 e ad altre architetture. Preferisce un progetto di migrazioni AnyCPU. Se le dipendenze in fase di progettazione richiedono un'architettura specifica, richiamare in modo esplicito un .NET SDK corrispondente.

L'architettura del processo in fase di progettazione è separata dalla destinazione di distribuzione. Quando si crea un bundle, usare --target-runtime o -TargetRuntime per generare un artefatto per il RID di distribuzione, ad esempio linux-arm64 o osx-arm64.