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

17

我在.NET Core 2.0上有一个小项目。由于Microsoft宣布将不再支持.NET Core 2.0,因此我尝试将该项目更新到最新版本,目前是3.1。但是,我在配置依赖注入时遇到了困难,需要一些帮助。

为了填充数据库,我需要获取所需的服务(如Db Context和用户配置),并将其传递给DbInitialize类,如下所示。我在Startup.cs配置之前在Program.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,但我找不到任何示例。

2个回答

19

我明白您想要获取一个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;
}

它创建了一种扩展的方法,可以让您在应用程序启动后自动升级数据库。它使用您的应用程序默认的服务提供程序来创建一个作用域并获取您的 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时,脚本不会被执行。您的数据库仍然是相同的。

希望这可以帮到您。


1
Anduin,这是我在数据填充主题中发现的最鼓舞人心的想法之一。非常感谢。 - Gerard Jaryczewski
1
这对非网络应用程序非常有帮助。 - Nae

5

Anduin的回答很好,添加扩展方法是一个不错的方式。如果您想要更少地更改现有代码来实现同样的功能,可以通过从Build的调用中获取ServiceProvider来实现这一点。这将返回一个IHost,它会公开IServiceProvider,您之前通过旧的BuildWebHost方法得到的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。


5
在.NET Core 5中,IHostBuilder没有Services属性。你如何调用GetService或GetRequiredService呢? - LukePerrin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接