25

I am trying to get an instance of the DbContext (so I can do some additional work upon startup with it), I get the following error when trying to get an instance in the Configure method:

System.InvalidOperationException: 'Cannot resolve scoped service 'MyApp.Data.MyDbContext' from root provider.'

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;
}

I can access an instance of the DbContext fine via the controller, etc

tomRedox
  • 28,092
  • 24
  • 117
  • 154
TheWebGuy
  • 11,429
  • 16
  • 52
  • 72
  • 3
    You can add dependencies to the Configure method and they will automatically get resolved. Try changing signature to public void Configure(IApplicationBuilder app, IHostingEnvironment env, MyDbContext dbContext) – Paul Hiles Aug 25 '17 at 15:50

3 Answers3

41

Paul Hiles comment is correct but that method works better in .NET Core 1.0.

In ASP.NET Core 2.0 it's generally a bad idea to run any database setup in Startup.cs. This is because if you run any migrations from the CLI or Visual Studio it will run all of Startup.cs and try to run your configuration which will fail. Of course if you don't use Entity-Framework then this isn't a problem however its still not the recommended way of doing it in 2.0. It's now recommended to do it in Program.cs.

For example you can create a extension method of IWebHost that will run any setup you need.

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;
}

And then in Program.cs you can then call that method before running.

public static void Main(string[] args)
{
    BuildWebHost(args)
        .MigrateDatabase()
        .Run();
}
Travis Boatman
  • 2,252
  • 16
  • 31
  • ...and if you are keeping all your DbContext items in separate a class library? Is there a way to set it up for the Asp.Net Core 2 Web app? – dinotom Apr 12 '18 at 21:02
  • Just sending you a comment to let you know this helped me big time with ASP.NET Core on Heroku. I wasn't able to run "heroku run dotnet ef database update", but I was able to use this code to have it run the migrations as it started up after I pushed to heroku master. I wrapped it in an environment variable (checked for equal to "true") so that I could control whether or not it ran, just in case I needed to. Works well with Heroku's web dashboard. – Matt Welke Apr 19 '18 at 02:25
  • How is this the accepted answer when it doesn't answer the question? I'm very confused. – DavidScherer Dec 04 '20 at 18:00
6

Update for Core 2.1 onwards

Just to add to @Travis Boatman's excellent answer, the preferred Main method syntax has changed slightly from Core 2.1 onwards and the default Main method now has CreateWebHostBuilder instead of BuildWebHost.

The revised code to call the extension method is shown below.

NB: the order is important here, the Build method returns a WebHost, which is what the extension method is extending, so you need to call the migrate method after Build() and before Run()):

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

Migrating more than one DbContext

We have more than one DbContext in our project, so I changed the extension method to a generic method that can take any type of 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;
}

You can then chain the calls to migrate the different contexts:

CreateWebHostBuilder(args)
    .Build()
    .MigrateDatabase<ApiAuthDbContext>()
    .MigrateDatabase<MainDbContext>()
    .MigrateDatabase<SomeOtherDbContext>()
    .Run();
tomRedox
  • 28,092
  • 24
  • 117
  • 154
  • CreateHostBuilder shows error " 'IHost' does not contain a definition for 'MigrateDatabase' and the best extension method overload 'Extensions.MigrateDatabase(IWebHost)' requires a receiver of type 'IWebHost' ". – rekker22 Sep 08 '20 at 21:13
  • Found the Solution, just change the MigrateDatabase method "IWebHost" to "IHost" and add "using Microsoft.Extensions.Hosting". – rekker22 Sep 09 '20 at 21:17
0

see this question and he answerd himself in the 'Update' section

Add in program.cs at CreateWebHostBuilder method

 .UseDefaultServiceProvider(options => {
     options.ValidateScopes = false;//to use any scoped
     option.validateOnBuild = false;//to use dbContext 
})

The full code:

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
    Host.CreateWebHostBuilder(args).ConfigureWebHostDefaults(webBuilder =>  
        {
            webBuilder.UseStartup<Startup>().UseDefaultServiceProvider(options =>
            {
                options.ValidateScopes = false;
                option.ValidateOnBuild = false;
            });
        })
}
WantToDo
  • 401
  • 1
  • 5
  • 11