Como gerenciar migrações

Conforme o modelo muda, as migrações são adicionadas e removidas como parte do desenvolvimento normal e os arquivos de migração são verificados no controle do código-fonte do projeto. Para gerenciar migrações, primeiro você deve instalar asFerramentas de linha de comando do EF Core.

Dica

Se DbContext está em um assembly diferente do projeto de inicialização, você pode especificar explicitamente o destino e os projetos de inicialização nas ferramentas do Console do Gerenciador de Pacotes ou nas ferramentas da CLI do .NET Core.

Adicionar uma migração

Depois que o modelo for alterado, você poderá adicionar uma migração para essa alteração:

dotnet ef migrations add AddBlogCreatedTimestamp

O nome da migração pode ser usado como uma mensagem de confirmação em um sistema de controle de versão. Por exemplo, você pode escolher um nome como AddBlogCreatedTimestamp se a alteração for uma nova propriedade CreatedTimestampna entidade Blog.

Três arquivos são adicionados ao seu projeto no diretório Migrações:

  • XXXXXXXXXXXXXX_AddCreatedTimestamp.cs-- O arquivo de migrações principal. Contém as operações necessárias para aplicar a migração (em Up) e revertê-la (em Down).
  • XXXXXXXXXXXXXX_AddCreatedTimestamp.Designer.cs-- O arquivo de metadados de migrações. Contém informações usadas pelo EF.
  • MyContextModelSnapshot.cs– um instantâneo do seu modelo atual. Usado para determinar o que mudou ao adicionar a próxima migração.

O carimbo de data/hora no nome de arquivo ajuda a mantê-lo organizado por ordem cronológica para que você possa ver o andamento das alterações.

Namespaces

É possível mover arquivos de Migrações e alterar o namespace deles manualmente. Novas migrações são criadas como irmãs da última migração. Como alternativa, você pode especificar o diretório em tempo de geração da seguinte maneira:

dotnet ef migrations add InitialCreate --output-dir Your/Directory

Observação

Você também pode alterar o namespace independentemente do diretório por meio de --namespace.

Personalizar o código de migração

Embora o EF Core geralmente crie migrações precisas, você sempre deve examinar o código e verificar se ele corresponde à alteração desejada. Em alguns casos, é até necessário fazer isso.

Renomeações de coluna

Um exemplo notável em que a personalização de migrações é necessária é ao renomear uma propriedade. Por exemplo, se você renomear uma propriedade de Name para FullName, o EF Core gerará a seguinte migração:

migrationBuilder.DropColumn(
    name: "Name",
    table: "Customers");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

O EF Core geralmente não consegue saber quando a intenção é remover uma coluna e criar uma nova (duas alterações separadas) e quando uma coluna deve ser renomeada. Se a migração acima for aplicada como está, todos os nomes de clientes serão perdidos. Para renomear uma coluna, substitua a migração gerada acima pelo seguinte:

migrationBuilder.RenameColumn(
    name: "Name",
    table: "Customers",
    newName: "FullName");

Dica

O processo de scaffolding da migração avisa quando uma operação puder resultar em perda de dados (como o descarte de uma coluna). Caso veja esse aviso, não deixe de examinar a precisão do código de migrações.

Como adicionar SQL bruto

Embora a renomeação de uma coluna possa ser obtida por meio de uma API interna, em muitos casos isso não é possível. Por exemplo, talvez queiramos substituir as propriedades existentes FirstName e LastName as propriedades por uma única propriedade FullName nova. A migração gerada pelo EF Core será a seguinte:

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customer");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customer");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customer",
    nullable: true);

Como antes, isso causaria perda de dados indesejada. Para transferir os dados das colunas antigas, reorganizamos as migrações e introduzimos uma operação SQL bruta da seguinte maneira:

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customer",
    nullable: true);

migrationBuilder.Sql(
@"
    UPDATE Customer
    SET FullName = FirstName + ' ' + LastName;
");

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customer");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customer");

Alterações arbitrárias por meio do SQL bruto

O SQL bruto também pode ser usado para gerenciar objetos de banco de dados que o EF Core não está ciente. Para fazer isso, adicione uma migração sem fazer nenhuma alteração de modelo; uma migração vazia será gerada, que você pode preencher com operações SQL brutas.

Por exemplo, a migração a seguir cria um procedimento armazenado do SQL Server:

migrationBuilder.Sql(
@"
    EXEC ('CREATE PROCEDURE getFullName
        @LastName nvarchar(50),
        @FirstName nvarchar(50)
    AS
        RETURN @LastName + @FirstName;')");

Dica

EXEC é usado quando uma instrução deve ser a primeira ou apenas uma em um lote SQL. Ele também pode ser usado para contornar erros de analisador em scripts de migração idempotentes que podem ocorrer quando colunas referenciadas não existem atualmente em uma tabela.

Isso pode ser usado para gerenciar qualquer aspecto do banco de dados, incluindo:

  • Procedimentos armazenados
  • Pesquisa de Texto Completo
  • Funções
  • Gatilhos
  • Exibições

Na maioria dos casos, o EF Core encapsulará automaticamente cada migração em sua própria transação ao aplicar migrações. Infelizmente, algumas operações de migrações não podem ser executadas em uma transação em alguns bancos de dados; para esses casos, você pode recusar a transação passando suppressTransaction: true para migrationBuilder.Sql.

Remover uma migração

Às vezes, você adiciona uma migração e percebe que precisa fazer alterações adicionais ao modelo do EF Core antes de aplicá-lo. Para remover a última migração, use este comando.

dotnet ef migrations remove

Após remover a migração, você poderá fazer as alterações adicionais ao modelo e adicioná-la novamente.

Aviso

Evite remover as migrações que já foram aplicadas aos bancos de dados de produção. Isso significa que você não poderá reverter essas migrações dos bancos de dados e pode quebrar as suposições feitas pelas migrações subsequentes.

Como listar migrações

Você pode listar todas as migrações existentes da seguinte maneira:

dotnet ef migrations list

Verificação de alterações pendentes no modelo

Observação

Esse recurso foi adicionado no EF Core 8.0.

Às vezes, você pode querer verificar se houve alguma alteração de modelo feita desde a última migração. Isso pode ajudá-lo a saber quando você ou um colega de equipe se esqueceu de adicionar uma migração. Uma maneira de fazer isso é usar este comando.

dotnet ef migrations has-pending-model-changes

Você também pode realizar essa verificação de forma programática usando context.Database.HasPendingModelChanges(). Isso pode ser usado para gravar um teste de unidade que falha quando você esquece de adicionar uma migração.

Redefinindo todas as migrações

Em alguns casos extremos, pode ser necessário remover todas as migrações e recomeçar. Isso pode ser feito facilmente excluindo sua pasta Migrações e descartando seu banco de dados.Nesse ponto, você pode criar uma nova migração inicial, que conterá todo o esquema atual.

Também é possível redefinir todas as migrações e criar uma única sem perder seus dados. Às vezes, isso é chamado de "esmagamento" e envolve um pouco de trabalho manual:

  1. Faça backup do seu banco de dados, caso algo dê errado.
  2. Em seu banco de dados, exclua todas as linhas da tabela de histórico de migrações (por exemplo, DELETE FROM [__EFMigrationsHistory] no SQL Server).
  3. Exclua sua pasta Migrações.
  4. Crie uma nova migração e gere um script SQL para ela (dotnet ef migrations script).
  5. Insira uma única linha no histórico de migrações para registrar que a primeira migração já foi aplicada, já que suas tabelas já estão lá. O SQL de inserção é a última operação no script SQL gerado acima e se parece com o seguinte (não se esqueça de atualizar os valores):
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

Aviso

Qualquer código de migração personalizado será perdido quando a pasta Migrações for excluída. Todas as personalizações devem ser aplicadas à nova migração inicial manualmente para serem preservadas.

Recursos adicionais