Propagação de dados

A propagação de dados é o processo de preencher um banco de dados com um conjunto inicial de dados.

Há várias maneiras de fazer isso no EF Core:

  • Propagação de Dados de Modelo
  • Personalização manual de migração
  • Lógica de inicialização personalizada

Propagação de Dados de Modelo

Ao contrário do EF6, no EF Core, a propagação de dados pode ser associada a um tipo de entidade como parte da configuração do modelo. Assim, as migrações do EF Core podem calcular automaticamente quais operações inserir, atualizar ou excluir precisam ser aplicadas ao atualizar o banco de dados para uma nova versão do modelo.

Observação

As migrações só consideram alterações de modelo ao determinar qual operação deve ser executada para obter os dados de semente no estado desejado. Portanto, quaisquer alterações nos dados executados fora das migrações podem ser perdidas ou causar um erro.

Como exemplo, isso configurará a propagação de dados para um Blog em OnModelCreating:

modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });

Para adicionar entidades que têm uma relação, os valores de chave estrangeira precisam ser especificados:

modelBuilder.Entity<Post>().HasData(
    new Post { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });

Se o tipo de entidade tiver alguma propriedade no estado de sombra, uma classe anônima poderá ser usada para fornecer os valores:

modelBuilder.Entity<Post>().HasData(
    new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });

Os tipos de entidade de propriedade podem ser propagados de maneira semelhante:

modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
    new { PostId = 1, First = "Andriy", Last = "Svyryd" },
    new { PostId = 2, First = "Diego", Last = "Vega" });

Consulte o projeto de exemplo completo para obter mais contexto.

Depois que os dados forem adicionados ao modelo, asmigrações deverão ser usadas para aplicar as alterações.

Dica

Se você precisar aplicar migrações como parte de uma implantação automatizada, poderá criar um script SQL que pode ser visualizado antes da execução.

Como alternativa, você pode usar context.Database.EnsureCreated() para criar um novo banco de dados contendo os dados de semente, por exemplo, para um banco de dados de teste ou ao usar o provedor na memória ou qualquer banco de dados não relacional. Observe que, se o banco de dados já existir, EnsureCreated() não atualizará o esquema nem os dados de propagação no banco de dados. Para bancos de dados relacionais, você não deve chamar EnsureCreated() se planeja usar migrações.

Limitações da propagação de dados de modelo

Esse tipo de propagação de dados é gerenciado por migrações e o script para atualizar os dados que já estão no banco de dados precisa ser gerado sem se conectar ao banco de dados. Isso impõe algumas restrições:

  • O valor da chave primária precisa ser especificado mesmo que geralmente seja gerado pelo banco de dados. Ele será usado para detectar alterações de dados entre migrações.
  • Os dados propagados anteriormente serão removidos se a chave primária for alterada de alguma forma.

Portanto, esse recurso é mais útil para dados estáticos que não devem ser alterados fora das migrações e não dependem de mais nada no banco de dados, por exemplo, CEP.

Se o cenário incluir qualquer um dos seguintes, é recomendável usar a lógica de inicialização personalizada descrita na última seção:

  • Dados temporários para teste
  • Dados que dependem do estado do banco de dados
  • Dados grandes (a propagação de dados é capturada em instantâneos de migração e dados grandes podem rapidamente levar a arquivos enormes e desempenho degradado).
  • Dados que precisam de valores de chave a serem gerados pelo banco de dados, incluindo entidades que usam chaves alternativas como a identidade
  • Dados que exigem transformação personalizada (que não é tratada por conversões de valor), como alguns hashs de senha
  • Dados que exigem chamadas à API externa, como ASP.NET funções de Identidade Principal e criação de usuários

Personalização manual de migração

Quando uma migração é adicionada, as alterações nos dados especificados com HasData são transformados em chamadas para InsertData(), UpdateData() e DeleteData(). Uma maneira de contornar algumas das limitações de HasData é adicionar manualmente essas chamadas ou operações personalizadas à migração.

migrationBuilder.InsertData(
    table: "Blogs",
    columns: new[] { "Url" },
    values: new object[] { "http://generated.com" });

Lógica de inicialização personalizada

Uma maneira simples e poderosa de executar a propagação de dados é usar DbContext.SaveChanges() antes que a lógica do aplicativo principal inicie a execução.

using (var context = new DataSeedingContext())
{
    context.Database.EnsureCreated();

    var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
    if (testBlog == null)
    {
        context.Blogs.Add(new Blog { Url = "http://test.com" });
    }

    context.SaveChanges();
}

Aviso

O código de propagação não deve fazer parte da execução normal do aplicativo, pois isso pode causar problemas de simultaneidade quando várias instâncias estão em execução e também exigiria que o aplicativo tivesse permissão para modificar o esquema de banco de dados.

Dependendo das restrições da implantação, o código de inicialização pode ser executado de maneiras diferentes:

  • Executando o aplicativo de inicialização localmente
  • Implantando o aplicativo de inicialização com o aplicativo principal, invocando a rotina de inicialização e desabilitando ou removendo o aplicativo de inicialização.

Isso geralmente pode ser automatizado usando perfis de publicação.