Aplicando migrações

Uma vez que suas migrações tenham sido adicionadas, elas precisam ser implantadas e aplicadas aos seus bancos de dados. Existem várias estratégias para fazer isso, sendo que algumas são mais apropriadas para ambientes de produção e outras para o ciclo de vida de desenvolvimento.

Note

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

Escolher uma estratégia de implantação

Para implantação automatizada, use um pacote de migração. Um pacote é um artefato de implantação que pode ser gerado em CI e executado posteriormente sem o SDK .NET, as ferramentas do EF Core ou o código-fonte do aplicativo. Use um script SQL quando o SQL precisar ser revisado, modificado, arquivado ou entregue a um DBA antes de ser aplicado.

Para desenvolvimento local ou dotnet ef database updateUpdate-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 da migração local e publicar pacotes ou scripts.

Strategy Uso recomendado Examinar o SQL antes da execução Requer SDK e origem em execução Usa o bloqueio de migração de EF Executa delegados de propagação de EF
Script SQL Implantação controlada ou controlada pelo DBA Yes No No No
Pacote de migração Implantação automatizada No No Yes Yes
Ferramentas de linha de comando do EF Desenvolvimento e teste locais No Yes Yes Yes
Migração de runtime Aplicativos que aceitam compensações de migração de inicialização No No Yes Yes

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

Use uma identidade separada para implantação que tenha permissão para alterar o esquema. A identidade usada pelo aplicativo em tempo de execução normalmente deve ter apenas as permissões que o aplicativo precisa para ler e gravar dados.

Scripts de SQL

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

  • Os scripts SQL podem ser revisados quanto à precisão; isso é importante, pois a aplicação de alterações de esquema aos 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 processo de CI.
  • Os scripts SQL podem ser fornecidos a um DBA e podem ser gerenciados e arquivados separadamente.

Uso básico

A seguir, um script SQL é gerado a partir de um banco de dados em branco para a migração mais recente:

dotnet ef migrations script

Por padrão, o comando grava o script na saída padrão. Use --output (ou -o) para criar um artefato de implantação com um nome previsível:

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

Com From (To implícito)

A seguir, um script SQL é gerado a partir da migração dada até a migração mais recente.

dotnet ef migrations script AddNewTables

Com From e To

A seguir, um script SQL é gerado a partir da migração de from especificada para a migração de to especificada.

dotnet ef migrations script AddNewTables AddAuditTable

É possível usar um from mais recente que o to para gerar um script de reversão.

Warning

Anote os possíveis cenários de perda de dados.

A geração de scripts aceita os seguintes dois argumentos para indicar o intervalo de migrações que será gerado:

  • A migração de 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 (esse é o padrão).
  • A migração para é a última migração que será aplicada ao banco de dados após a execução do script. O padrão é a última migração em seu projeto.

Os scripts de migração atualizam um banco de dados existente. Provisione o próprio banco de dados por meio de sua implantação de infraestrutura ou processo de administração de banco de dados antes de aplicar o script. A criação de banco de dados normalmente requer uma conexã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 a bancos de dados no estado de migração correto. O Entity Framework Core também dá suporte à geração de scripts idempotentes, que verificam internamente quais migrações já foram aplicadas (por meio da tabela do 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 em uma migração diferente.

O suporte a script idempotente depende do provedor de banco de dados. Por exemplo, o SQLite atualmente não dá suporte à geração de scripts de migração idempotentes.

O seguinte gera migrações idempotentes:

dotnet ef migrations script --idempotent

Ferramentas da linha de comando

As ferramentas de linha de comando do EF podem ser utilizadas para aplicar migrações a um banco de dados. Embora seja produtiva para o desenvolvimento local e o teste de migrações, essa abordagem não é ideal para o gerenciamento de 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 do .NET e a ferramenta EF devem ser instalados em servidores de produção e exigem o código-fonte do projeto.

As seguintes instruções atualizam seu banco de dados para a última migração:

dotnet ef database update

A seguir, atualize seu banco de dados para uma determinada migração:

dotnet ef database update AddNewTables

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

Warning

Anote os possíveis cenários de perda de dados.

Para obter mais informações sobre a aplicação de migrações por meio das ferramentas de linha de comando, consulte a referência de Ferramentas do Entity Framework Core.

Ambiente e configuração

As ferramentas executam o código do aplicativo para construir o DbContext. A seleção do provedor, as cadeias de conexão e a configuração do modelo podem, portanto, depender do ambiente do aplicativo. As ferramentas de tempo de design do EF Core usam o Development ambiente quando nem nem ASPNETCORE_ENVIRONMENTDOTNET_ENVIRONMENT é definido.

Defina o ambiente explicitamente ao gerar um artefato de implantação e ao executar um pacote. 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 em um shell compatível com POSIX:

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

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

Isso também impede que um pacote carregue segredos do usuário de desenvolvimento inesperadamente. Um ambiente padrão mais seguro para pacotes é acompanhado por dotnet/efcore#36188. A seleção de ambiente no Visual Studio experiência de publicação é controlada por dotnet/efcore#11950.

Não armazene cadeias de conexão de produção no controle do código-fonte ou insira-as no pacote. Forneça a conexão de implantação do repositório secreto do sistema de implantação. A identidade de implantação deve ter permissões de esquema; a identidade normal do aplicativo geralmente não deve.

Bundles

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

  • A execução de scripts SQL exige ferramentas adicionais.
  • O tratamento de transações e o comportamento de continuar 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 as migrações.
  • Os pacotes podem ser gerados como parte de seu processo de CI e facilmente executados posteriormente como parte do seu processo de implantação.
  • Os pacotes podem ser executados sem instalar o SDK .NET ou a ferramenta EF. Quando autossuficientes, eles não exigem o código-fonte do projeto ou mesmo o .NET Runtime.
  • Os pacotes usam o bloqueio de migração do EF Core e executam a lógica configurada UseSeeding .

Ao contrário de um script SQL, um pacote não fornece atualmente uma maneira de inspecionar o SQL que ele executará ou listar as migrações que ele contém. Se sua implantação exigir a revisão do SQL, gere um script. As melhorias na inspeção de pacotes são controladas por dotnet/efcore#25872.

A seguir, um pacote é gerado:

dotnet ef migrations bundle --output artifacts/efbundle

A seguir, um pacote autônomo é gerado para o Linux:

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

Para obter mais informações sobre a criação de pacotes, consulte a Referência de ferramentas do Entity Framework Core.

efbundle

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

Arguments:

Argument Description
<MIGRATION> A migração alvo. Se for '0', todas as migrações serão revertidas. O padrão é a última migração.

Options:

Option Short Description
--connection <CONNECTION> A cadeia de conexão para o banco de dados. O padrão é aquele especificado em AddDbContext ou OnConfiguring.
--verbose -v Mostrar a saída detalhada.
--no-color Não colorir a saída.
--prefix-output Prefixar a saída com o nível.

O exemplo a seguir aplica migrações a uma instância de SQL Server local usando o nome de usuário e as credenciais especificados:

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

Para reverter o banco de dados, passe 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'

Warning

Uma reversão executa as Down operações de cada migração mais recente que o destino e pode resultar em perda de dados. Examine e teste o comportamento de reversão antes de usá-lo nos dados de produção.

O código de propagação configurado é executado após um downgrade. Ele deve tolerar o esquema da migração de destino, incluindo um esquema de aplicativo ausente quando o destino for 0.

Warning

Se a configuração de contexto for leitura appsettings.json, copie os arquivos de configurações necessários junto com o pacote. Os arquivos de configuração são resolvidos no diretório de execução do pacote. Não coloque segredos de produção nesses arquivos; forneça-os por meio de uma origem de configuração segura ou da opção --connection .

Contêineres e trabalhos de implantação

Gere o pacote durante o build e execute-o como um trabalho de implantação de um tiro depois que o banco de dados estiver íntegro. Não instale o SDK ou execute dotnet ef na imagem do aplicativo e não faça com que todas as réplicas de aplicativo executem migrações de seu ponto de entrada. Configure a plataforma de implantação para não reiniciar o contêiner de migração depois que ele for encerrado com êxito.

Para aplicativos Aspire, AddEFMigrations pode coordenar migrações durante o desenvolvimento local. Durante a publicação, PublishAsMigrationBundle pode emitir um pacote ou uma imagem de contêiner e PublishAsMigrationScript pode emitir um script SQL. Consulte Aplicar migrações do EF Core no Aspire para uma configuração de trabalho de uma captura para Aplicativos de Contêiner do Azure, Docker Compose e Kubernetes.

O exemplo de pacote de migração demonstra duas migrações sqlite, propagação idempotente, aplicativo de encaminhamento e propagação segura de reversão.

Exemplo de pacote de migração

Um pacote precisa de migrações para incluir. Elas são criadas utilizando dotnet ef migrations add conforme descrito em Crie sua primeira migração. Uma vez que você tenha migrações prontas para implantar, crie um pacote usando 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 ao seu sistema operacional de destino. No meu caso, como é Windows x64, eu recebo um efbundle.exe colocado na minha pasta local. A execução desse 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>

Assim como ocorre 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, a execução do mesmo pacote novamente não tem nenhum efeito, pois 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>

Entretanto, 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>

Tip

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

A execução desse novo pacote aplica essas 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 pacote usa a cadeia de conexão do banco de dados da configuração do aplicativo. No entanto, um banco de dados diferente pode ser migrado passando o cadeia de conexão na linha de comando. 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>

Note

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


Aplicar migrações em runtime

É possível que o próprio aplicativo aplique migrações de forma programática, normalmente durante a inicialização. O EF Core 9 e posteriores protegem a execução da migração com um bloqueio em todo o banco de dados, portanto, isso pode ser aceitável para aplicativos que preferem a implantação simples e podem tolerar o comportamento de migração de inicialização. Uma etapa de implantação de migração separada ainda é preferencial quando a revisão, as credenciais de privilégio mínimo, a distribuição coordenada ou a alta disponibilidade são importantes.

Considere as seguintes compensações:

  • Para versões do EF anteriores à 9, se várias instâncias do aplicativo estiverem em execução, os dois 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 graves problemas.
  • O aplicativo deve ter acesso elevado para modificar o esquema do banco de dados. Em geral, é uma boa prática limitar as permissões do banco de dados do aplicativo na produção.
  • É importante poder reverter uma migração aplicada no caso de um problema. As outras estratégias oferecem isso de forma fácil e imediata.
  • 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 de forma programática, chame context.Database.MigrateAsync(). Por exemplo, um aplicativo ASP.NET típico 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() é construído sobre o serviço IMigrator, que pode ser utilizado em cenários mais avançados. Use myDbContext.GetInfrastructure().GetService<IMigrator>() para acessá-lo.

Warning

  • Considere cuidadosamente antes de utilizar essa abordagem na produção. Prefira um pacote de migração para automação ou um script SQL quando a revisão e a aprovação forem necessárias.
  • Não chame EnsureCreatedAsync() antes de MigrateAsync(). O EnsureCreatedAsync() ignora as Migrações para criar o esquema e causa falha no MigrateAsync().

Bloqueio de migração

Começando com o EF Core 9, MigrateAsync e Migrate adquirem automaticamente um bloqueio em todo o banco de dados antes de aplicar as migrações. Isso protege contra corrupção de banco de dados que pode resultar de várias instâncias de aplicativo executando migrações simultaneamente, o que é um cenário comum ao aplicar migrações em runtime. O bloqueio é mantido durante a execução da migração, incluindo qualquer código de semeadura, e é liberado automaticamente quando a operação é concluída.

O bloqueio de migração se aplica 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, pois são aplicados fora do EF Core.

Note

A partir do EF Core 9, chamar Migrate() ou MigrateAsync() gerará uma exceção quando o modelo tiver alterações pendentes em relação à última migração (ID do evento de aviso RelationalEventId.PendingModelChangesWarning). Para detectar essa condição antes da implantação, use o comando dotnet ef migrations has-pending-model-changes em seu pipeline de CI/CD. O aviso pode ser suprimido por meio ConfigureWarnings (ignorando RelationalEventId.PendingModelChangesWarning) se necessário, mas isso geralmente não é recomendado em cenários de produção. Consulte a nota sobre alteração significativa para obter mais informações.

Warning

O mecanismo de bloqueio varia significativamente entre os provedores de banco de dados e pode envolver problemas específicos do provedor. Por exemplo, o provedor SQLite usa uma tabela de bloqueio que pode ser abandonada se o processo for encerrado inesperadamente. Consulte sempre a documentação do provedor para obter detalhes.

Limitações