Aplicando migrações

Depois que as migrações forem adicionadas, elas precisarão ser implantadas e aplicadas aos bancos de dados. Existem várias estratégias para fazer isso, sendo algumas mais apropriadas para ambientes de produção e outras para o ciclo de vida de desenvolvimento.

Observação

Seja qual for sua estratégia de implantação, sempre inspecione as migrações geradas e teste-as antes de aplicar a um banco de dados de produção. Uma migração pode soltar uma coluna quando a intenção era renomeá-la ou pode falhar por vários motivos quando aplicada a um banco de dados.

Escolha uma estratégia de implantação

Para implementação automatizada, utilize um pacote de migração. Um bundle é um artefacto de implementação que pode ser gerado em CI e executado posteriormente sem o SDK .NET, as ferramentas EF Core ou o código-fonte da aplicação. Use um script SQL em vez disso quando o SQL tiver de ser revisto, modificado, arquivado ou entregue a um DBA antes de ser aplicado.

Para desenvolvimento local, dotnet ef database update ou Update-Database é geralmente a opção mais simples. Os projetos Aspire devem usar a integração de migrações do Aspire EF Core para coordenar a execução local da migração e publicar bundles ou scripts.

Strategy Utilização recomendada Revise o SQL antes da execução Requer SDK e código-fonte na execução Utiliza bloqueio de migração EF Executa delegados de cabeça de série EF
Script SQL Implementação controlada por DBA ou com revisões bloqueadas Yes No No No
Feixe de migração Implantação automatizada No No Yes Yes
Ferramentas de linha de comandos EF Desenvolvimento local e testes No Yes Yes Yes
Migração em tempo de execução Aplicações que aceitam compromissos de migração de startups No No Yes Yes

O EF Core 9 e posteriores usam bloqueio de migração. Operações e ferramentas síncronas invocam UseSeeding; operações assíncronas invocam UseAsyncSeeding.

Use uma identidade separada para a implementação que tenha permissão para alterar o esquema. A identidade usada pela aplicação em tempo de execução deve normalmente ter apenas as permissões que a aplicação necessita para ler e escrever dados.

Scripts de SQL

Os scripts SQL são recomendados quando o processo de implementação exige que o SQL gerado seja inspecionado ou alterado antes da execução. As vantagens desta estratégia incluem o seguinte:

  • Os scripts SQL podem ser revisados quanto à precisão; Isso é importante, pois aplicar alterações de esquema a bancos de dados de produção é uma operação potencialmente perigosa que pode envolver perda de dados.
  • Em alguns casos, os scripts podem ser ajustados para atender às necessidades específicas de um banco de dados de produção.
  • Os scripts SQL podem ser usados em conjunto com uma tecnologia de implantação e podem até mesmo ser gerados como parte do seu processo de CI.
  • Os scripts SQL podem ser fornecidos a um DBA e podem ser gerenciados e arquivados separadamente.

Utilização Básica

O seguinte gera um script SQL de um banco de dados em branco para a migração mais recente:

dotnet ef migrations script

Por defeito, o comando escreve o script para saída padrão. Use --output (ou -o) para criar um artefacto de implementação com um nome previsível:

dotnet ef migrations script --idempotent --output artifacts/migrations.sql

De... a... (para implícito)

O seguinte gera um script SQL da migração dada para a migração mais recente.

dotnet ef migrations script AddNewTables

Com De e Para

O que se segue gera um script de SQL da migração especificada de from para a migração especificada de to.

dotnet ef migrations script AddNewTables AddAuditTable

Você pode usar um from mais recente do que o to para gerar um script de reversão.

Advertência

Por favor, tome nota de possíveis cenários de perda de dados.

A geração de scripts aceita os dois argumentos a seguir para indicar qual intervalo de migrações deve ser gerado:

  • O da migração deve ser a última migração aplicada ao banco de dados antes de executar o script. Se nenhuma migração tiver sido aplicada, especifique 0 (este é o padrão).
  • O para migração é a última migração que será aplicada ao banco de dados após a execução do script. Por padrão, é utilizada a última migração no seu projeto.

Os scripts de migração atualizam uma base de dados existente. Provisione a própria base de dados através do processo de implementação da infraestrutura ou administração da base de dados antes de aplicar o script. A criação de bases de dados normalmente requer uma ligação diferente, permissões elevadas e configuração específica do provedor.

Scripts SQL idempotentes

Os scripts SQL gerados acima só podem ser aplicados para alterar seu esquema de uma migração para outra; É sua responsabilidade aplicar o script adequadamente e somente aos bancos de dados no estado de migração correto. O EF Core também suporta a geração de scripts idempotentes , que verificam internamente quais migrações já foram aplicadas (por meio da tabela de histórico de migrações) e aplicam apenas as ausentes. Isso é útil se você não souber exatamente qual foi a última migração aplicada ao banco de dados ou se estiver implantando em vários bancos de dados que podem estar cada um em uma migração diferente.

O suporte a scripts idempotentes depende do fornecedor da base de dados. Por exemplo, o SQLite atualmente não suporta gerar scripts de migração idempotentes.

O seguinte gera migrações idempotentes:

dotnet ef migrations script --idempotent

Ferramentas de linha de comando

As ferramentas de linha de comando do EF podem ser usadas para aplicar migrações a um banco de dados. Embora produtiva para o desenvolvimento local e teste de migrações, essa abordagem não é ideal para gerenciar bancos de dados de produção:

  • Os comandos SQL são aplicados diretamente pela ferramenta, sem dar ao desenvolvedor a chance de inspecioná-los ou modificá-los. Isso pode ser perigoso em um ambiente de produção.
  • O SDK .NET e a ferramenta EF devem ser instalados em servidores de produção e requerem o código-fonte do projeto.

O seguinte atualiza seu banco de dados para a migração mais recente:

dotnet ef database update

O seguinte atualiza seu banco de dados para uma determinada migração:

dotnet ef database update AddNewTables

Observe que isso também pode ser usado para reverter para uma migração anterior.

Advertência

Por favor, tome nota de possíveis cenários de perda de dados.

Para obter mais informações sobre como aplicar migrações através das ferramentas de linha de comandos, consulte a referência das ferramentas do EF Core .

Ambiente e configuração

As ferramentas executam código de aplicação para construir o DbContext. A seleção do fornecedor, cadeias de ligação e configuração do modelo podem, portanto, depender do ambiente da aplicação. As ferramentas de design do EF Core utilizam o Development ambiente quando nem ASPNETCORE_ENVIRONMENT está DOTNET_ENVIRONMENT definido.

Defina o ambiente explicitamente ao gerar um artefacto de implementação e ao executar um bundle. Por exemplo, no PowerShell:

$env:ASPNETCORE_ENVIRONMENT = 'Production'
dotnet ef migrations bundle --output artifacts\efbundle.exe
$env:ASPNETCORE_ENVIRONMENT = 'Production'
.\efbundle.exe --connection $env:DEPLOYMENT_CONNECTION_STRING

Ou numa carcaça compatível com POSIX:

ASPNETCORE_ENVIRONMENT=Production \
    dotnet ef migrations bundle --output artifacts/efbundle

ASPNETCORE_ENVIRONMENT=Production \
    ./efbundle --connection "$DEPLOYMENT_CONNECTION_STRING"

Isto também impede que um bundle carregue secretos de utilizador de desenvolvimento de forma inesperada. Um ambiente padrão mais seguro para bundles é rastreado pela dotnet/efcore#36188. A seleção do ambiente na experiência de publicação do Visual Studio é acompanhada por dotnet/efcore#11950.

Não guardes cadeias de ligação de produção no controlo de versões nem as incorpores no bundle. Forneça a ligação de implantação a partir do armazenamento secreto do sistema de implantação. A identidade de implementação deve ter permissões de esquema; A identidade normal da aplicação normalmente não deveria.

Pacotes

Os pacotes de migração são executáveis de arquivo único que podem ser usados para aplicar migrações a um banco de dados. Eles abordam algumas das deficiências do script SQL e das ferramentas de linha de comando:

  • A execução de scripts SQL requer ferramentas adicionais.
  • O manuseio de transações e o comportamento de continuação em caso de erro dessas ferramentas são inconsistentes e, às vezes, inesperados. Isso pode deixar seu banco de dados em um estado indefinido se ocorrer uma falha ao aplicar migrações.
  • Os pacotes podem ser gerados como parte do seu processo de CI e facilmente executados posteriormente como parte do seu processo de implantação.
  • Bundles podem ser executados sem instalar o SDK .NET ou a ferramenta EF (ou mesmo o runtime .NET, quando autónomo), e não requerem o código-fonte do projeto.
  • Os bundles utilizam o bloqueio de migração do EF Core e executam lógica configurada UseSeeding .

Ao contrário de um script SQL, um bundle atualmente não fornece uma forma de inspecionar o SQL que irá executar nem de listar as migrações que contém. Se a sua implementação exigir revisão SQL, gere um script em vez disso. As melhorias na inspeção de pacotes são acompanhadas pela dotnet/efcore#25872.

O seguinte gera um pacote:

dotnet ef migrations bundle --output artifacts/efbundle

O seguinte gera um pacote autônomo para Linux:

dotnet ef migrations bundle --self-contained --target-runtime linux-x64 --output artifacts/efbundle

Para obter mais informações sobre como criar conjuntos, consulte a referência das ferramentas do EF Core .

efbundle

O executável resultante é nomeado efbundle por padrão. Ele pode ser usado para atualizar o banco de dados para a migração mais recente. É equivalente a executar dotnet ef database update ou Update-Database.

Argumentos:

Argumento Descrição
<MIGRATION> A migração alvo. Se '0', todas as migrações serão revertidas. Define-se como padrão a última migração.

Opções:

Opção Curto Descrição
--connection <CONNECTION> Connection string para a base de dados. Assume o valor predefinido especificado em AddDbContext ou OnConfiguring.
--verbose -v Mostrar saída detalhada.
--no-color Não colorir a saída.
--prefix-output Prefixar a saída com o nível.

O exemplo seguinte aplica migrações para uma instância local do SQL Server usando o nome de utilizador e credenciais especificados:

.\efbundle.exe --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;User ID=myUsername;Password={;'$Credential;'here'}'

Para reverter a base de dados, passar a migração que deve permanecer aplicada. A passagem 0 reverte todas as migrações:

.\efbundle.exe PreviousMigration --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;Integrated Security=True'
.\efbundle.exe 0 --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;Integrated Security=True'

Advertência

Um rollback executa as Down operações de todas as migrações mais recentes do que o destino e pode resultar em perda de dados. Revise e teste o comportamento de rollback antes de o usar em dados de produção.

O código de seed configurado corre após um downgrade. Deve tolerar o esquema da migração do destino, incluindo um esquema de aplicação em falta quando o destino é 0.

Advertência

Se a configuração de contexto ler appsettings.json, copie os ficheiros de definições necessários juntamente com o pacote. Os ficheiros de configuração são resolvidos a partir do diretório de execução do pacote. Não coloque segredos de produção nestes ficheiros; Forneça-os através de uma fonte de configuração segura ou da --connection opção.

Contentores e empregos de implantação

Gera o bundle durante a build e executa-o como um trabalho de deployment one-shot depois de a base de dados estar saudável. Não instales o SDK nem executes dotnet ef na imagem da aplicação, e não faças com que todas as réplicas de aplicação executem migrações a partir do seu ponto de entrada. Configure a plataforma de implementação para não reiniciar o contentor de migração depois de este sair com sucesso.

Para aplicações Aspire, AddEFMigrations pode coordenar migrações durante o desenvolvimento local. Durante a publicação, PublishAsMigrationBundle pode emitir um bundle ou uma imagem de contentor, e PublishAsMigrationScript pode emitir um script SQL. Veja Apply EF Core migrations no Aspire para configuração one-shot de jobs para Azure Container Apps, Docker Compose e Kubernetes.

O exemplo do pacote de migração demonstra duas migrações SQLite: seed de idempotentes, forward application e rollback-safe seeding.

Exemplo de pacote de migração

Um pacote precisa de migrações para ser incluído. Eles são criados usando o dotnet ef migrations add, conforme descrito em Crie a sua primeira migração. Depois de ter migrações prontas para implantação, crie um pacote usando o dotnet ef migrations bundle. Por exemplo:

PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>

A saída é um executável adequado para o seu sistema operacional de destino. No meu caso, isto é Windows x64, por isso recebo um efbundle.exe colocado na minha pasta local. A execução deste executável aplica as migrações contidas nele:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903083845_MyMigration'.
Done.
PS C:\local\AllTogetherNow\SixOh>

Tal como acontece com dotnet ef database update ou Update-Database, as migrações são aplicadas ao banco de dados somente se ainda não tiverem sido aplicadas. Por exemplo, executar o mesmo pacote novamente não faz nada, já que não há novas migrações a serem aplicadas:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
No migrations were applied. The database is already up to date.
Done.
PS C:\local\AllTogetherNow\SixOh>

No entanto, se forem feitas alterações no modelo e mais migrações forem geradas com dotnet ef migrations add, elas poderão ser agrupadas em um novo executável pronto para ser aplicado. Por exemplo:

PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add SecondMigration
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add Number3
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle --force
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>

Dica

A opção --force pode ser usada para substituir o pacote existente por um novo.

A execução deste novo pacote aplica estas duas novas migrações ao banco de dados:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>

Por padrão, o bundle utiliza a string de conexão da base de dados da configuração da sua aplicação. No entanto, uma base de dados diferente pode ser migrada passando a cadeia de ligação na linha de comandos. Por exemplo:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe --connection "Data Source=(LocalDb)\MSSQLLocalDB;Database=SixOhProduction"
Applying migration '20210903083845_MyMigration'.
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>

Observação

Desta vez, as três migrações foram aplicadas, uma vez que nenhuma delas ainda havia sido aplicada ao banco de dados de produção.


Aplicar migrações em tempo de execução

É possível que o próprio aplicativo aplique migrações programaticamente, normalmente durante a inicialização. O EF Core 9 e posteriores protegem a execução de migração com um bloqueio a nível de base de dados, pelo que isto pode ser aceitável para aplicações que preferem implementação simples e toleram comportamentos de migração no arranque. Uma etapa separada de implementação da migração continua a ser preferida quando é importante, credenciais de privilégio mínimo, implementação coordenada ou alta disponibilidade.

Considere os seguintes compromissos:

  • Para versões do EF anteriores a 9, se várias instâncias do seu aplicativo estiverem em execução, ambos os aplicativos poderão tentar aplicar a migração simultaneamente e falhar (ou pior, causar corrupção de dados).
  • Da mesma forma, se um aplicativo estiver acessando o banco de dados enquanto outro aplicativo o migra, isso pode causar problemas graves.
  • O aplicativo deve ter acesso elevado para modificar o esquema do banco de dados. Geralmente, é uma boa prática limitar as permissões do banco de dados do aplicativo em produção.
  • É importante poder reverter uma migração aplicada em caso de problema. As outras estratégias fornecem isso facilmente e fora da caixa.
  • Os comandos SQL são aplicados diretamente pelo programa, sem dar ao desenvolvedor a chance de inspecioná-los ou modificá-los. Isso pode ser perigoso em um ambiente de produção.

Para aplicar migrações programaticamente, chame context.Database.MigrateAsync(). Por exemplo, uma aplicação ASP.NET típica pode fazer o seguinte:

public static async Task Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        await db.Database.MigrateAsync();
    }

    host.Run();
}

Observe que MigrateAsync() se baseia no serviço IMigrator, que pode ser usado para cenários mais avançados. Use myDbContext.GetInfrastructure().GetService<IMigrator>() para acessá-lo.

Advertência

  • Considere cuidadosamente antes de usar essa abordagem na produção. Prefiro um pacote de migração para automação ou um script SQL quando for necessária revisão e aprovação.
  • Não ligue para EnsureCreatedAsync() antes de MigrateAsync(). EnsureCreatedAsync() ignora Migrações para criar o esquema, o que faz com que MigrateAsync() falhe.

Bloqueio de migração

Começando com o EF Core 9, MigrateAsync e Migrate adquirem automaticamente um bloqueio da base de dados antes de aplicar qualquer migração. Isto protege contra corrupção da base de dados que pode resultar de múltiplas instâncias de aplicação a executar migrações em simultâneo, o que é um cenário comum ao aplicar migrações em tempo de execução. O bloqueio é mantido durante toda a execução da migração, incluindo qualquer código de seeding, e é automaticamente libertado quando a operação termina.

O bloqueio de migração aplica-se quando as migrações são aplicadas usando qualquer um dos seguintes métodos:

Os scripts SQL não são afetados pelo bloqueio de migração, uma vez que são aplicados fora do EF Core.

Observação

A partir da versão 9 do EF Core, a chamada de Migrate() ou MigrateAsync() lançará uma exceção quando o modelo tiver alterações pendentes relativamente à última migração (ID do evento de aviso RelationalEventId.PendingModelChangesWarning). Para detetar esta condição antes da implementação, use o dotnet ef migrations has-pending-model-changes comando no seu pipeline CI/CD. O aviso pode ser suprimido ( ConfigureWarnings ignorando RelationalEventId.PendingModelChangesWarning) se necessário, mas isto geralmente não é recomendado em cenários de produção. Consulte a nota de alteração de última hora para mais informações.

Advertência

O mecanismo de bloqueio varia significativamente entre fornecedores de bases de dados e pode envolver questões específicas de cada fornecedor. Por exemplo, o fornecedor SQLite utiliza uma tabela de bloqueio que pode ser abandonada se o processo terminar inesperadamente. Consulte sempre a documentação do seu fornecedor para mais detalhes.

Limitações