Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Migrações Code First é a forma recomendada de evoluir o esquema da base de dados da sua aplicação se estiver a usar o fluxo de trabalho Code First. As migrações fornecem um conjunto de ferramentas que permitem:
- Crie uma base de dados inicial que funcione com o seu modelo EF
- Gerar migrações para acompanhar as alterações que faz no seu modelo EF
- Mantenha a sua base de dados atualizada com essas alterações
A seguinte guia fornecerá uma visão geral das Migrações Code First no Entity Framework. Podes completar todo o guia ou saltar para o tema que te interessa. São abordados os seguintes tópicos:
Construção de um Modelo Inicial e Base de Dados
Antes de começarmos a usar migrações, precisamos de um projeto e de um modelo Code First para trabalhar. Para este passo a passo, vamos usar o modelo canónico Blog e Post.
- Criar uma nova aplicação MigrationsDemo Console
- Adicionar a versão mais recente do pacote NuGet do EntityFramework ao projeto
- Ferramentas –> Gestor de Pacotes da Biblioteca –> Consola do Gestor de Pacotes
- Execute o comando Install-Package EntityFramework
- Adicione um ficheiro Model.cs com o código mostrado abaixo. Este código define uma única classe Blog que compõe o nosso modelo de domínio e uma classe BlogContext que é o nosso contexto EF Code First
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
- Agora que temos um modelo, é altura de o usar para aceder a dados. Atualize o ficheiro Program.cs com o código mostrado abaixo.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Execute a sua aplicação e verá que uma base de dados MigrationsCodeDemo.BlogContext foi criada para si.
Possibilitação de Migrações
Está na hora de fazer mais algumas alterações ao nosso modelo.
- Vamos introduzir uma propriedade Url na classe Blog.
public string Url { get; set; }
Se voltar a executar a aplicação, receberia uma InvalidOperationException a indicar que o modelo que serve de base para o contexto 'BlogContext' foi alterado desde que a base de dados foi criada. Considere usar Code First Migrations para atualizar a base de dados (http://go.microsoft.com/fwlink/?LinkId=238269).
Como a exceção sugere, está na hora de começar a usar as Migrações Code First. O primeiro passo é permitir migrações para o nosso contexto.
Execute o comando Enable-Migrations na Consola do Gestor de Pacotes
Este comando adicionou uma pasta Migrations ao nosso projeto. Esta nova pasta contém dois ficheiros:
A classe de Configuração. Esta classe permite-lhe configurar como o Migrations se comporta no seu contexto. Para este guia, vamos usar apenas a configuração padrão. Como existe apenas um único contexto Code First no seu projeto, Enable-Migrations preencheu automaticamente o tipo de contexto a que esta configuração se aplica.
Uma migração InicialCrear. Esta migração foi gerada porque já tínhamos o Code First criado uma base de dados para nós, antes de ativarmos as migrações. O código nesta migração estruturada representa os objetos que já foram criados na base de dados. No nosso caso, essa é a tabela do Blog com as colunas BlogId e Nome . O nome do ficheiro inclui um carimbo temporal para ajudar na ordenação. Se a base de dados ainda não tivesse sido criada, esta migração do InitialCreate não teria sido adicionada ao projeto. Em vez disso, a primeira vez que chamamos Add-Migration o código para criar estas tabelas seria estruturado para uma nova migração.
Múltiplos modelos direcionados para a mesma base de dados
Ao utilizar versões anteriores ao EF6, apenas um modelo Code First podia ser usado para gerar/gerir o esquema de uma base de dados. Isto resulta de uma única tabela __MigrationsHistory por base de dados, sem forma de identificar quais as entradas que pertencem a que modelo.
A partir do EF6, a classe Configuration inclui uma propriedade ContextKey . Isto funciona como um identificador único para cada modelo Code First. Uma coluna correspondente na tabela __MigrationsHistory permite que entradas de múltiplos modelos partilhem a tabela. Por predefinição, esta propriedade está definida para o nome totalmente qualificado do seu contexto.
Geração e Execução de Migrações
O Code First Migrations tem dois comandos principais com os quais vais ficar familiarizado.
- Add-Migration irá gerar a próxima migração com base nas alterações que foram feitas ao seu modelo desde a última migração
- O Update-Database aplicará quaisquer migrações pendentes à base de dados
Precisamos de estruturar uma migração para tratar da nova propriedade url que adicionámos. O comando Add-Migration permite-nos dar um nome a estas migrações, vamos simplesmente chamar à nossa AddBlogUrl.
- Execute o comando Add-Migration AddBlogUrl na Console do Gestor de Pacotes
- Na pasta Migrações temos agora uma nova migração AddBlogUrl . O nome do ficheiro de migração é prefixado com um carimbo temporal para ajudar na encomenda
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddBlogUrl : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("dbo.Blogs", "Url");
}
}
}
Agora podemos editar ou acrescentar a esta migração, mas parece estar tudo bastante bem. Vamos usar o Update-Database para aplicar esta migração à base de dados.
- Execute o comando Update-Database na Consola do Gestor de Pacotes
- O Code First Migrations irá comparar as migrações na nossa pasta Migrations com as que foram aplicadas à base de dados. Verá que a migração AddBlogUrl precisa ser aplicada e executá-la.
A base de dados MigrationsDemo.BlogContext está agora atualizada para incluir a coluna Url na tabela Blogs .
Personalização de Migrações
Até agora gerámos e executámos uma migração sem fazer quaisquer alterações. Agora vamos ver como editar o código que é gerado por defeito.
- Está na altura de fazer mais algumas alterações ao nosso modelo, vamos adicionar uma nova propriedade de Classificação à classe Blog
public int Rating { get; set; }
- Vamos também adicionar uma nova classe Post
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- Vamos também adicionar uma coleção de Publicações à classe de Blogue para formar o outro extremo da relação entre Blogue e Publicação
public virtual List<Post> Posts { get; set; }
Vamos usar o comando Add-Migration para deixar o Code First Migrations fazer a sua melhor hipótese de migração para nós. Vamos chamar a esta migração AddPostClass.
- Execute o comando Add-Migration AddPostClass na Consola do Gestor de Pacotes.
A Code First Migrations fez um bom trabalho a estruturar estas mudanças, mas há algumas coisas que talvez queiramos alterar:
- Primeiro, vamos adicionar um índice único à coluna Posts.Title (adicionando as linhas 22 e 29 no código abaixo).
- Também estamos a adicionar uma coluna Blogs.Rating não anulável. Se houver algum dado existente na tabela, será atribuído o valor padrão CLR do tipo de dados para a nova coluna (sendo Classificação um inteiro, isso seria 0). Mas queremos especificar um valor padrão de 3 para que as linhas existentes na tabela de Blogs comecem com uma classificação razoável. (Pode ver o valor padrão especificado na linha 24 do código abaixo)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Posts",
c => new
{
PostId = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 200),
Content = c.String(),
BlogId = c.Int(nullable: false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey("dbo.Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropIndex("dbo.Posts", new[] { "Title" });
DropIndex("dbo.Posts", new[] { "BlogId" });
DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs");
DropColumn("dbo.Blogs", "Rating");
DropTable("dbo.Posts");
}
}
}
A nossa migração editada está pronta, por isso vamos usar Update-Database para trazer a base de dados atualizada. Desta vez, vamos especificar a flag –Verbose para que possas ver o SQL que o Code First Migrations está a executar.
- Execute o comando Update-Database –Verbose na Consola do Gestor de Pacotes.
Data Motion / SQL Personalizado
Até agora analisámos operações de migração que não alteram nem movem dados, agora vamos ver algo que precisa de mover alguns dados. Ainda não há suporte nativo para movimento de dados, mas podemos executar alguns comandos SQL arbitrários em qualquer ponto do nosso script.
- Vamos adicionar uma propriedade Post.Abstract ao nosso modelo. Mais tarde, vamos pré-preencher o Resumo para publicações existentes usando algum texto do início da coluna de Conteúdo .
public string Abstract { get; set; }
Vamos usar o comando Add-Migration para deixar o Code First Migrations fazer a sua melhor hipótese de migração para nós.
- Execute o comando Add-Migration AddPostAbstract na Consola do Gestor de Pacotes.
- A migração gerada trata das alterações de esquema, mas também queremos pré-preencher a coluna Abstrato usando os primeiros 100 caracteres de conteúdo para cada publicação. Podemos fazer isto descendo para SQL e executando uma instrução UPDATE depois de a coluna ser adicionada. (Adicionando a linha 12 no código abaixo)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostAbstract : DbMigration
{
public override void Up()
{
AddColumn("dbo.Posts", "Abstract", c => c.String());
Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
public override void Down()
{
DropColumn("dbo.Posts", "Abstract");
}
}
}
A nossa migração editada está a ir bem, por isso vamos usar Update-Database para trazer a base de dados atualizada. Vamos especificar a flag –Verbose para que possamos ver o SQL a ser executado contra a base de dados.
- Execute o comando Update-Database –Verbose na Consola do Gestor de Pacotes.
Migrar para uma versão específica (incluindo downgrade)
Até agora, sempre atualizámos para a migração mais recente, mas pode haver momentos em que queiras atualizar/fazer downgrade para uma migração específica.
Imaginemos que queremos migrar a nossa base de dados para o estado em que estava depois de executar a migração do AddBlogUrl . Podemos usar a opção –TargetMigration para realizar o downgrade para esta migração.
- Execute o comando Update-Database –TargetMigration: AddBlogUrl na Consola do Gestor de Pacotes.
Este comando executará o script Down para as nossas migrações AddBlogAbstract e AddPostClass .
Se quiseres voltar completamente para uma base de dados vazia, podes usar o comandoUpdate-Database – TargetMigration: $InitialDatabase .
Obter um script SQL
Se outro programador quiser estas alterações na sua máquina, pode simplesmente sincronizar assim que verificarmos as alterações no controlo de versão. Quando tiverem as nossas novas migrações, podem simplesmente executar o comando Update-Database para aplicar as alterações localmente. No entanto, se quisermos enviar estas alterações para um servidor de testes e, eventualmente, para produção, provavelmente queremos um script SQL que possamos entregar ao nosso DBA.
- Execute o comando Update-Database , mas desta vez especifique a flag –Script para que as alterações sejam escritas num script em vez de aplicadas. Vamos também especificar uma origem e um destino para a migração, a fim de gerar o script. Queremos que um script vá de uma base de dados vazia ($InitialDatabase) para a versão mais recente ( migração AddPostAbstract). Se não especificar uma migração alvo, as Migrações usarão a migração mais recente como destino. Se não especificar uma migração de código-fonte, as migrações usarão o estado atual da base de dados.
- Execute o comando Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract no Consola do Gestor de Pacotes
O Code First Migrations executa o pipeline de migração, mas em vez de aplicar as alterações, escreve-as num ficheiro .sql por si. Assim que o script é gerado, ele é aberto para si no Visual Studio, pronto para visualizar ou guardar.
Geração de Scripts Idempotentes
A partir do EF6, se especificar –SourceMigration $InitialDatabase, então o script gerado será 'idempotente'. Scripts idempotentes podem atualizar uma base de dados atualmente em qualquer versão para a versão mais recente (ou para a versão especificada, se usar –TargetMigration). O script gerado inclui lógica para verificar a tabela __MigrationsHistory e aplicar apenas alterações que não tenham sido aplicadas anteriormente.
Atualização Automática no Arranque da Aplicação (MigrateDatabaseToLatestVersion Initializer)
Se estiver a implementar a sua aplicação, pode querer que ela atualize automaticamente a base de dados (aplicando quaisquer migrações pendentes) quando a aplicação for iniciada. Pode fazer isto registando o inicializador de base de dados MigrateDatabaseToLatestVersion. Um inicializador de base de dados contém simplesmente alguma lógica que é usada para garantir que a base de dados está configurada corretamente. Esta lógica é executada na primeira utilização do contexto dentro do processo de aplicação (AppDomain).
Podemos atualizar o ficheiro Program.cs , como mostrado abaixo, para definir o inicializador MigrateDatabaseToLatestVersion para o BlogContext antes de usarmos o contexto (Linha 14). Note que também precisa adicionar uma instrução using para o namespace System.Data.Entity (Linha 5).
Quando criamos uma instância deste inicializador, precisamos de especificar o tipo de contexto (BlogContext) e a configuração das migrações (Configuração) – a configuração das migrações é a classe que foi adicionada à nossa pasta Migrações quando ativamos as Migrações.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MigrationsDemo.Migrations;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Agora, sempre que a nossa aplicação é executada, verifica primeiro se a base de dados que está a usar está atualizada e aplica quaisquer migrações pendentes caso não esteja.