ASP.NET Core 2 + Получить экземпляр db-контекста

Я пытаюсь получить экземпляр DbContext (так что я могу сделать некоторые дополнительные работы при запуске с ним), я получаю следующую ошибку при попытке получить экземпляр в методе Configure:

System.InvalidOperationException: "Не удается разрешить ограниченную службу" MyApp.Data.MyDbContext "от корневого провайдера.

public void ConfigureServices(IServiceCollection services)
{
 services.AddDbContext<MyDbContext>(
                options => options.UseSqlServer(Configuration.GetConnectionString("MyDbContext")));
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{

    var dbContext = app.ApplicationServices.GetService(typeof(MyDbContext)) as MyDbContext;
}

Я могу получить доступ к экземпляру штрафа DbContext через контроллер и т.д.

Ответ 1

Комментарий Пол Хейлса верен, но этот метод лучше работает в .NET Core 1.0.

В ASP.NET Core 2.0, как правило, плохая идея запускать любую настройку базы данных в Startup.cs. Это связано с тем, что если вы выполните какие-либо миграции из CLI или Visual Studio, он запустит все Startup.cs и попытается запустить вашу конфигурацию, которая завершится с ошибкой. Конечно, если вы не используете Entity-Framework, то это не проблема, однако ее еще не рекомендуется использовать в версии 2.0. Теперь рекомендуется сделать это в Program.cs.

Например, вы можете создать метод расширения IWebHost, который будет запускать любую необходимую настройку.

public static IWebHost MigrateDatabase(this IWebHost webHost)
{
    var serviceScopeFactory = (IServiceScopeFactory)webHost.Services.GetService(typeof(IServiceScopeFactory));

    using (var scope = serviceScopeFactory.CreateScope())
    {
        var services = scope.ServiceProvider;
        var dbContext = services.GetRequiredService<YourDbContext>();

        dbContext.Database.Migrate();
    }

    return webHost;
}

И затем в Program.cs вы можете вызвать этот метод перед запуском.

public static void Main(string[] args)
{
    BuildWebHost(args)
        .MigrateDatabase()
        .Run();
}

Ответ 2

Просто чтобы добавить к превосходному ответу @Travis, предпочтительный синтаксис метода Main немного изменился по сравнению с Core 2.1 и теперь Main метод по умолчанию теперь имеет CreateWebHostBuilder вместо BuildWebHost.

Пересмотренный код для вызова метода расширения показан ниже.

NB: здесь важен порядок, метод Build возвращает WebHost, который расширяет метод расширения, поэтому вам нужно вызвать метод migrate после Build() и перед Run()):

public static void Main(string[] args)
{
    CreateWebHostBuilder(args)
        .Build()
        .MigrateDatabase()
        .Run();
}

В нашем проекте более одного DbContext, поэтому я изменил метод расширения на универсальный метод, который может принимать любой тип DbContext:

public static IWebHost MigrateDatabase<T>(this IWebHost webHost) where T:DbContext
{
    var serviceScopeFactory = (IServiceScopeFactory)webHost
        .Services.GetService(typeof(IServiceScopeFactory));

    using (var scope = serviceScopeFactory.CreateScope())
    {
        var services = scope.ServiceProvider;

        var dbContext = services.GetRequiredService<T>();
        dbContext.Database.Migrate();
    }

    return webHost;
}

Затем вы можете объединить вызовы для переноса различных контекстов:

CreateWebHostBuilder(args)
    .Build()
    .MigrateDatabase<ApiAuthDbContext>()
    .MigrateDatabase<MainDbContext>()
    .MigrateDatabase<SomeOtherDbContext>()
    .Run();