Aplicando as 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 pessoas para o ciclo de vida de desenvolvimento.

Observação

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.

Scripts SQL

A maneira recomendada de implantar migrações em um banco de dados de produção é gerando scripts SQL. 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

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.

Aviso

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.

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 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 do EF devem ser instalados nos servidores de produção e exigem o código-fonte do projeto.

Observação

Cada migração é aplicada na sua própria transação. Confira Problema do GitHub nº 22616 para ver uma discussão sobre os possíveis aprimoramentos futuros nessa área.

A seguir, as atualizações do seu banco de dados para a migração mais recente:

dotnet ef database update

A seguir, as atualizações do 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.

Aviso

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.

Pacotes

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 com o 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 a instalação do SDK do .NET ou da Ferramenta do EF (ou mesmo do Runtime do .NET, quando autônomos), e não exigem o código-fonte do projeto.

A seguir, um pacote é gerado:

dotnet ef migrations bundle

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

dotnet ef migrations bundle --self-contained -r linux-x64

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.

Argumentos:

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

Opções:

Opção Short Descrição
--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 do SQL Server local utilizando o nome de usuário e a senha especificados.

.\efbundle.exe --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;User ID=myUsername;Password=myPassword'

Aviso

Não se esqueça de copiar appsettings.json junto com o pacote. O pacote depende da presença de appsettings.json no diretório de execuçã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, trata-se do Windows x64, de modo que recebo um efbundle.exe removido em 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>

Dica

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 utiliza a cadeia de conexões de banco de dados da configuração do seu aplicativo. Entretanto, um banco de dados diferente pode ser migrado passando a 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>

Observação

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. Embora seja produtiva para o desenvolvimento local e o teste de migrações, essa abordagem é inadequada para o gerenciamento de bancos de dados de produção, pelos seguintes motivos:

  • Se várias instâncias do seu aplicativo estiverem sendo executadas, ambos os aplicativos poderão tentar aplicar a migração ao mesmo tempo 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.Migrate(). Por exemplo, um aplicativo ASP.NET típico pode fazer o seguinte:

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

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

    host.Run();
}

Observe que Migrate() é 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.

Aviso

  • Considere cuidadosamente antes de utilizar essa abordagem na produção. A experiência mostrou que a simplicidade dessa estratégia de implantação é superada pelos problemas que ela cria. Ao invés disso, considere a possibilidade de gerar scripts SQL a partir de migrações.
  • Não chame EnsureCreated() antes de Migrate(). O EnsureCreated() ignora as Migrações para criar o esquema e causa falha no Migrate().