criar banco de dados automaticamente no Entity Framework Core

100

Meu aplicativo que está sendo portado para o .NET core usará o novo EF Core com SQLite. Desejo criar automaticamente o banco de dados e as estruturas de tabela quando o aplicativo é executado pela primeira vez. De acordo com a documentação do núcleo EF, isso é feito usando comandos manuais

dotnet ef migrations add MyFirstMigration

dotnet ef database update

No entanto, não quero que o usuário final insira esses comandos e prefiro que o aplicativo crie e configure o banco de dados para o primeiro uso. Para EF 6, há funcionalidades como

Database.SetInitializer(new CreateDatabaseIfNotExists<MyContext>());

Mas no EF Core eles não parecem existir. Não consigo encontrar nenhum exemplo ou documentação sobre algo equivalente para o núcleo EF e isso não é mencionado na lista de recursos ausentes na documentação do núcleo EF. Eu já tenho as classes do modelo configuradas, então eu poderia escrever algum código para inicializar o banco de dados com base nos modelos, mas seria muito mais fácil se o framework fizesse isso automaticamente. Não quero construir automaticamente o modelo ou migrar, apenas criar as estruturas da tabela em um novo banco de dados.

Estou faltando alguma coisa aqui ou a função de criação automática de tabela está faltando no núcleo EF?

deandob
fonte

Respostas:

146

Se você criou as migrações, pode executá-las no Startup.cs da seguinte maneira.

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.Migrate();
      }

      ...

Isso criará o banco de dados e as tabelas usando suas migrações adicionadas.

Se você não estiver usando o Entity Framework Migrations e, em vez disso, apenas precisar que seu modelo DbContext seja criado exatamente como está em sua classe de contexto na primeira execução, você pode usar:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 {
      using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
      {
            var context = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
            context.Database.EnsureCreated();
      }

      ...

Em vez de.

Se você precisar excluir seu banco de dados antes de garantir que ele seja criado, chame:

            context.Database.EnsureDeleted();

Um pouco antes de você ligar EnsureCreated()

Adaptado de: http://docs.identityserver.io/en/latest/quickstarts/7_entity_framework.html?highlight=entity

Ricardo Fontana
fonte
1
Ainda sou muito novo no EF, criei as classes que definem as estruturas de dados usando o código do EF 6 primeiro "criar do banco de dados" do Visual Studio usando um arquivo de banco de dados atual. Em seguida, recortei / colei-os na nova solução dotnet core VS - então acho que não são migrações. Isso significa que devo criar um arquivo de migração antes que o código acima possa ser usado?
deandob
2
Desculpe, cometi um erro na minha resposta, se você não estiver usando migrações, pode usar o comando context.Database.EnsureCreated () / EnsureDeleted (), mais detalhes em blogs.msdn.microsoft.com/dotnet/2016 / 09/29 /…
Ricardo Fontana
2
E se você precisar das duas soluções? Acabamos de portar nosso aplicativo para UWP e começamos a usar o EF Core, o que significa que alguns usuários precisam que o banco de dados seja criado do zero, enquanto outros já têm o banco de dados. Existe alguma maneira de garantir que a primeira migração crie apenas as tabelas iniciais se elas ainda não existirem? Sua resposta não parece cobrir isso ou estou faltando alguma coisa?
Lars Udengaard
33

Minha resposta é muito semelhante à de Ricardo, mas sinto que minha abordagem é um pouco mais direta simplesmente porque há tanta coisa acontecendo em sua usingfunção que nem tenho certeza de como funciona exatamente em um nível inferior.

Portanto, para aqueles que desejam uma solução simples e limpa que crie um banco de dados onde você saiba exatamente o que está acontecendo nos bastidores, isto é para você:

public Startup(IHostingEnvironment env)
{
    using (var client = new TargetsContext())
    {
        client.Database.EnsureCreated();
    }
}

Isso significa basicamente que dentro do DbContextque você criou (neste caso, o meu é chamado TargetsContext), você pode usar uma instância de DbContextpara garantir que as tabelas definidas com na classe sejam criadas quando Startup.cs for executado em seu aplicativo.

susieloo_
fonte
10
Só como informação a partir daqui : EnsureCreatedignora totalmente as migrações e apenas cria o esquema para você, você não pode misturar isso com migrações. EnsureCreatedfoi projetado para teste ou prototipagem rápida em que você não tem problemas em descartar e recriar o banco de dados a cada vez. Se você estiver usando migrações e quiser que elas sejam aplicadas automaticamente na inicialização do aplicativo, você pode usar context.Database.Migrate().
Thomas Schneiter
Isso responde à minha confusão por que minha string de conexão não está funcionando ... :( Então, o banco de dados não foi criado automaticamente, você deve especificar Database.EnsureCreated () que fiz no meu construtor DbContext. Muito obrigado, me salva do dilema de 3 dias. X_X
pampi
Só queria observar que tentei colar isso em meu arquivo de inicialização e o VS2019 me informou que IHostingEnvironmentagora está obsoleto e a alternativa recomendada é Microsoft.AspNetCore.Hosting.IWebHostEnvironment.
ctrl-z pls
18

Se você obtiver o contexto por meio da lista de parâmetros de Configure em Startup.cs, você pode fazer o seguinte:

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  LoggerFactory loggerFactory,
    ApplicationDbContext context)
 {
      context.Database.Migrate();
      ...
John Pankowicz
fonte
1
IMHO esta é a melhor solução: você não precisa se preocupar com nenhum serviço ou mesmo com o contexto do banco de dados, você apenas pode usar o contexto que você obtém através da injeção de dependência. Agradável!
Tobias
12

Para EF Core 2.0+ eu tive que adotar uma abordagem diferente porque eles mudaram a API. A partir de março de 2019, a Microsoft recomenda que você coloque o código de migração do banco de dados na classe de entrada do aplicativo, mas fora do código de compilação do WebHost.

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();
        using (var serviceScope = host.Services.CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetRequiredService<PersonContext>();
            context.Database.Migrate();
        }
        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}
Paul Stegler
fonte
7

Se você não criou migrações, existem 2 opções

1. criar o banco de dados e as tabelas do aplicativo principal:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();

2. criar as tabelas se o banco de dados já existir:

var context = services.GetRequiredService<YourRepository>();
context.Database.EnsureCreated();
RelationalDatabaseCreator databaseCreator =
(RelationalDatabaseCreator)context.Database.GetService<IDatabaseCreator>();
databaseCreator.CreateTables();

Graças à resposta do Bubi

Nathan Alard
fonte
2
quais são os seus serviços?
MichaelMao
Toda a documentação que estou lendo shows de grandes avisos de gordura em torno de migrações dizendo "Não use EnsureCreatedou EnsureDeletedou Database.Migrateirá falhar"
mwilson