How do I get a instance of a service in ASP.NET Core 3.1

12,840

Solution 1

I understand that you want to get an instance of your DBContext. And execute some code with it. Right here I will give you an example to auto seed the database during ASP.NET Core 3.1 start up. You can try the following code or try the way of soloing your problem.

First, copy the following code to your project:

public static IHost MigrateDbContext<TContext>(this IHost host) where TContext : DbContext
{
    // Create a scope to get scoped services.
    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        var logger = services.GetRequiredService<ILogger<TContext>>();
        // get the service provider and db context.
        var context = services.GetService<TContext>();

        // do something you can customize.
        // For example, I will migrate the database.
        context.Database.Migrate();
    }

    return host;
}

It creates an extended method for IHost which allows you to upgrade your database automatically after the application starts. It uses your application's default service provider to create a scope and get your DBContext. And try to migrate the database to the latest status.

If your database is empty or does not exists at all, the script can also create your database automatically.

Finally, use the extend method in your startup process. Like this:

public static void Main(string[] args)
{
    CreateHostBuilder(args)
        .Build()
        .MigrateDbContext<WikiDbContext>() // <-- call it here like this.
        .Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}

Try to start your application, and monitor if it can successfully execute the update process.

When you are executing other ef commands like dotnet ef migrations add Test and the script will not be executed. Your database is still the same.

Hope this helps.

Solution 2

Anduin's answer is good and adding the extension method is a nice way to do it. You can achieve the same with less changes to your existing code if that's what you want by getting the ServiceProvider from the call to Build. This returns an IHost which exposes the IServiceProvider which you previously got through the IWebHost returned by the old BuildWebHost method.

public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        
    using( var scope = host.Services.CreateScope() )
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<GameStoreContext>();
            var configuration = services.GetRequiredService<IConfiguration>();
            var userManager = services.GetRequiredService<UserManager<IdentityUser>>();
            var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
            DbInitializer.Initialize(context, configuration, userManager, roleManager).GetAwaiter().GetResult();
        }
        catch( Exception ex )
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }

        host.Run();
    }

Then you can seed the database and call host.Run afterwards as before.

Share:
12,840

Related videos on Youtube

Leonardo De Lima Ferreira
Author by

Leonardo De Lima Ferreira

Updated on June 04, 2022

Comments

  • Leonardo De Lima Ferreira
    Leonardo De Lima Ferreira almost 2 years

    I have a small project in .NET Core 2.0 and as Microsoft announced that would no longer support .NET Core 2.0 I tried to update the project to the latest version at this time is 3.1. But I had a hard time configuring the Dependency Injection and need some help.

    In order to populate the database, I need to get required services such as Db Context and user configuration and pass this on to DbInitialize class as shown below. I did this in Program.cs before Startup.cs configuration.

    public class Program {
            public static void Main(string[] args)
            {
    
                var host = BuildWebHost(args);
    
                using (var scope = host.Services.CreateScope())
                {
                    var services = scope.ServiceProvider;
                    try
                    {
                        var context = services.GetRequiredService<GameStoreContext>();
                        var configuration = services.GetRequiredService<IConfiguration>();
                        var userManager = services.GetRequiredService<UserManager<IdentityUser>>();
                        var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
                        DbInitializer.Initialize(context, configuration, userManager,roleManager).GetAwaiter().GetResult();
                    }
                    catch (Exception ex)
                    {
                        var logger = services.GetRequiredService<ILogger<Program>>();
                        logger.LogError(ex, "An error occurred while seeding the database.");
                    }
                }
                host.Run();
            }
    
            public static IWebHost BuildWebHost(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .Build();
        }
    

    But in the .NET Core 3.1 version BuildWebHost can no longer be instantiated so I was unable to retrieve services like this.

    The new version of Program.cs in .NET Core 3.1 looks like this

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
    

    There is any way that I can achieve the same result in the new Framework?

    OBS: I have read in some posts people advising to use IConfiguration but I couldn't find any example.

  • LukePerrin
    LukePerrin over 3 years
    IHostBuilder doesnt have the Services property in .net core 5. How do you call GetService or GetRequiredService?
  • Gerard Jaryczewski
    Gerard Jaryczewski about 3 years
    Anduin, it is one of the most inspiring ideas which I've found in the topic of data seeding. Thank you very much.
  • Nae
    Nae about 3 years
    This is very helpful for non-web applications.