Compartilhar via


Migrações do Code First com um banco de dados existente

Observação

Somente a partir do EF4.3 – os recursos, APIs e etc. discutidos nesta página foram introduzidos no Entity Framework 4.1. Se você estiver usando uma versão anterior, algumas ou todas as informações não se aplicarão.

Este artigo aborda o uso das Migrações do Code First com um banco de dados existente, um que não foi criado pelo Entity Framework.

Observação

Este artigo pressupõe que você sabe usar as Migrações do Code First em cenários básicos. Caso contrário, será necessário ler Migrações do Code First antes de continuar.

Etapa 1: criar um modelo

Sua primeira etapa será criar um modelo do Code First direcionado ao banco de dados existente. O tópico Code First para um banco de dados existente fornece diretrizes detalhadas sobre como fazer isso.

Observação

É importante seguir o restante das etapas deste tópico antes de fazer qualquer alteração no modelo que exija alterações no esquema de banco de dados. As etapas a seguir exigem que o modelo esteja em sincronia com o esquema de banco de dados.

Etapa 2: habilitar as Migrações

A próxima etapa é habilitar as migrações. Você pode fazer isso executando o comando Enable-Migrations no console do gerenciador de pacotes.

Esse comando criará uma pasta em sua solução chamada Migrações e colocará uma única classe dentro dela chamada Configuração. A classe Configuração é onde você configura as Migrações para o seu aplicativo; você pode saber mais sobre ela no tópico Migrações do Code First.

Etapa 3: adicionar uma migração inicial

Depois que as migrações tiverem sido criadas e aplicadas ao banco de dados local, talvez você também queira aplicar essas alterações a outros bancos de dados. Por exemplo, seu banco de dados local pode ser um banco de dados de teste e, em última análise, você pode querer aplicar também as alterações a um banco de dados de produção e/ou a outros desenvolvedores que testam bancos de dados. Há duas opções para esta etapa, e a que você deve escolher depende se o esquema de outros bancos de dados está ou não vazio ou corresponde atualmente ao esquema do banco de dados local.

  • Opção 1: use o esquema existente como ponto de partida. Você deve usar essa abordagem quando outros bancos de dados aos quais as migrações serão aplicadas no futuro terão o mesmo esquema que o banco de dados local atualmente. Por exemplo, você poderá usá-la se o banco de dados de teste local corresponder atualmente à v1 do banco de dados de produção e, posteriormente, aplicar essas migrações para atualizar o banco de dados de produção para v2.
  • Opção 2: usar o banco de dados vazio como ponto de partida. Você deve usar essa abordagem quando outros bancos de dados aos quais as migrações serão aplicadas no futuro estiverem vazios (ou ainda não existirem). Por exemplo, você poderá usá-la se começar a desenvolver seu aplicativo usando um banco de dados de teste, mas sem usar migrações e, posteriormente, desejar criar um banco de dados de produção do zero.

Opção 1: usar o esquema existente como ponto de partida

As Migrações do Code First usam um instantâneo do modelo armazenado na migração mais recente para detectar alterações no modelo (você pode encontrar informações detalhadas sobre isso em Migrações do Code First em ambientes de equipe). Como vamos assumir que os bancos de dados já têm o esquema do modelo atual, geraremos uma migração vazia (sem operações) que tem o modelo atual como um instantâneo.

  1. Execute o comando Add-Migration InitialCreate –IgnoreChanges no console do gerenciador de pacotes. Isso cria uma migração vazia com o modelo atual como um instantâneo.
  2. Execute o comando Update-Database no console do gerenciador de pacotes. Isso aplicará a migração InitialCreate ao banco de dados. Como a migração real não contém nenhuma alteração, ela simplesmente adicionará uma linha à tabela __MigrationsHistory indicando que essa migração já foi aplicada.

Opção 2: usar o banco de dados vazio como ponto de partida

Nesse cenário, precisamos que as Migrações possam criar todo o banco de dados do zero – incluindo as tabelas que já estão presentes em nosso banco de dados local. Vamos gerar uma migração InitialCreate que inclui lógica para criar o esquema existente. Em seguida, faremos com que pareça que essa migração já foi aplicada ao nosso banco de dados existente.

  1. Execute o comando Add-Migration InitialCreate no console do gerenciador de pacotes. Isso cria uma migração para criar o esquema existente.
  2. Comente todo o código no método Up da migração recém-criada. Isso nos permitirá "aplicar" a migração para o banco de dados local sem tentar recriar todas as tabelas e etc. que já existem.
  3. Execute o comando Update-Database no console do gerenciador de pacotes. Isso aplicará a migração InitialCreate ao banco de dados. Como a migração real não contém alterações (porque as comentamos temporariamente), ela simplesmente adicionará uma linha à tabela __MigrationsHistory indicando que essa migração já foi aplicada.
  4. Cancele o comentário do código no método Up. Isso significa que, quando essa migração for aplicada a bancos de dados futuros, o esquema que já existia no banco de dados local será criado por migrações.

Considerações

Há algumas coisas sobre as quais você precisa estar ciente ao usar as Migrações em um banco de dados existente.

Nomes padrão/calculados podem não corresponder ao esquema existente

As Migrações especificam explicitamente nomes para colunas e tabelas quando ela faz scaffolding de uma migração. No entanto, há outros objetos de banco de dados para os quais as Migrações calculam um nome padrão ao aplicar as migrações. Isso inclui índices e restrições de chave estrangeira. Ao direcionar um esquema existente, esses nomes calculados podem não corresponder ao que realmente existe em seu banco de dados.

Aqui estão alguns exemplos de quando você precisa estar ciente disso:

Se você usou a "Opção 1: usar o esquema existente como ponto de partida" da Etapa 3:

  • Se alterações futuras em seu modelo exigirem a alteração ou remoção de um dos objetos de banco de dados nomeados de forma diferente, você precisará modificar a migração com scaffolding para especificar o nome correto. As APIs de Migrações têm um parâmetro de nome opcional que permite que você faça isso. Por exemplo, seu esquema existente pode ter uma tabela Post com uma coluna de chave estrangeira BlogId que tem um índice chamado IndexFk_BlogId. No entanto, por padrão, as Migrações esperariam que esse índice fosse nomeado IX_BlogId. Se você fizer uma alteração no modelo que resulta na remoção desse índice, será necessário modificar a chamada dropIndex com scaffolding para especificar o nome de IndexFk_BlogId.

Se você usou "Opção 2: usar banco de dados vazio como ponto de partida" da Etapa 3:

  • A tentativa de executar o método Down da migração inicial (ou seja, reverter para um banco de dados vazio) em relação ao banco de dados local pode falhar porque as Migrações tentarão remover índices e restrições de chave estrangeira usando os nomes incorretos. Isso só afetará seu banco de dados local, pois outros bancos de dados serão criados do zero usando o método Up da migração inicial. Se você quiser fazer downgrade do banco de dados local existente para um estado vazio, é mais fácil fazer isso manualmente, descartando o banco de dados ou descartando todas as tabelas. Após esse downgrade inicial, todos os objetos de banco de dados serão recriados com os nomes padrão; portanto, esse problema não se apresentará novamente.
  • Se alterações futuras em seu modelo exigirem a alteração ou a remoção de um dos objetos de banco de dados nomeados de forma diferente, isso não funcionará em relação ao banco de dados local existente, pois os nomes não corresponderão aos padrões. No entanto, isso funcionará em relação aos bancos de dados que foram criados "do zero", pois eles terão usado os nomes padrão escolhidos pelas Migrações. Você pode fazer essas alterações manualmente em seu banco de dados local existente ou considerar fazer com que as Migrações recriem seu banco de dados do zero, como em outros computadores.
  • Os bancos de dados criados usando o método Up da migração inicial podem ser ligeiramente diferentes do banco de dados local, pois os nomes padrão calculados para índices e restrições de chave estrangeira serão usados. Você também pode acabar com índices extras, pois as Migrações criarão índices em colunas de chave estrangeira por padrão. Isso pode não ter sido o caso no banco de dados local original.

Nem todos os objetos de banco de dados são representados no modelo

Objetos de banco de dados que não fazem parte do modelo não serão tratados pelas Migrações. Isso pode incluir exibições, procedimentos armazenados, permissões, tabelas que não fazem parte do modelo, índices adicionais etc.

Aqui estão alguns exemplos de quando você precisa estar ciente disso:

  • Independentemente da opção que você escolheu na "Etapa 3", se alterações futuras no modelo exigirem alterar ou descartar esses objetos adicionais, as Migrações não saberão fazer essas alterações. Por exemplo, se você soltar uma coluna que tenha um índice adicional, as Migrações não saberão descartar o índice. Você precisará adicionar isso manualmente à migração com scaffolding.
  • Se você usou "Opção 2: usar um banco de dados vazio como ponto de partida", esses objetos adicionais não serão criados pelo método Up da migração inicial. Você pode modificar os métodos Up e Down para cuidar desses objetos adicionais, se desejar. Para objetos que não têm suporte nativo na API de Migrações, como exibições, você pode usar o método Sql para executar o SQL bruto para criá-los/soltá-los.