如何在 ASP.NET Core 3.1 中获取服务实例

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

我有一个 .NET Core 2.0 的小项目,随着微软宣布不再支持 .NET Core 2.0,我尝试将项目更新到最新版本,此时是 3.1。但是我很难配置依赖注入,需要一些帮助。

为了填充数据库,我需要获取所需的服务,例如 Db 上下文和用户配置,并将其传递给 DbInitialize class,如下所示。我在 Program.cs 配置 Startup.cs 之前做了这个。

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

但是在 .NET Core 3.1 版本中,BuildWebHost 无法再被实例化,所以我无法像这样检索服务。

.NET Core 3.1 中 Program.cs 的新版本如下所示

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

有什么方法可以在新框架中实现相同的结果?

OBS:我在一些帖子中看到有人建议使用 IConfiguration,但我找不到任何示例。

我了解到您想获取 DBContext 的一个实例。并用它执行一些代码。在这里我会给你一个在 ASP.NET Core 3.1 启动期间自动为数据库做种的例子。您可以尝试以下代码或尝试解决问题的方法。

首先,将以下代码复制到您的项目中:

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

它为 IHost 创建了一个扩展方法,允许您在应用程序启动后自动升级数据库。它使用您应用程序的默认服务提供者来创建范围并获取您的 DBContext。并尝试将数据库迁移到最新状态。

如果您的数据库为空或根本不存在,脚本也可以自动创建您的数据库。

最后,在启动过程中使用扩展方法。像这样:

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

尝试启动您的应用程序,并监视它是否可以成功执行更新过程。

当您执行其他 ef 命令如 dotnet ef migrations add Test 时,脚本将不会被执行。你的数据库还是一样的。

希望对您有所帮助。

Anduin 的回答很好,添加扩展方法是一个很好的方法。如果您希望通过调用 Build 获取 ServiceProvider,您可以通过对现有代码进行较少的更改来实现相同的目的。此 returns 一个 IHost,它公开了您之前通过旧 BuildWebHost 方法返回的 IWebHost 获得的 IServiceProvider。

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

然后您可以为数据库做种,然后像以前一样调用 host.Run。