Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core 2 + Get instance of db context

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

like image 935
TheWebGuy Avatar asked Aug 25 '17 15:08

TheWebGuy


People also ask

What is DbContext in .NET Core?

A DbContext instance represents a session with the database and can be used to query and save instances of your entities. DbContext is a combination of the Unit Of Work and Repository patterns. Entity Framework Core does not support multiple parallel operations being run on the same DbContext instance.

What is AddDbContext?

AddDbContext<TContext>(IServiceCollection, ServiceLifetime) Registers the given context as a service in the IServiceCollection. You use this method when using dependency injection in your application, such as with ASP.NET.

Is DbContext a singleton?

First, DbContext is a lightweight object; it is designed to be used once per business transaction. Making your DbContext a Singleton and reusing it throughout the application can cause other problems, like concurrency and memory leak issues. And the DbContext class is not thread safe.

Is DbContext scoped?

This example registers a DbContext subclass called ApplicationDbContext as a scoped service in the ASP.NET Core application service provider (a.k.a. the dependency injection container). The context is configured to use the SQL Server database provider and will read the connection string from ASP.NET Core configuration.


2 Answers

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(); } 
like image 200
Travis Boatman Avatar answered Sep 28 '22 02:09

Travis Boatman


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(); 
like image 23
tomRedox Avatar answered Sep 28 '22 02:09

tomRedox