首页 > 解决方案 > 如何在没有 IHostedService 的情况下正确使用 .Net Core Generic Host?

问题描述

我们有遗留应用程序,我们计划在未来某个时间过渡到 ASP.NET Core API (v2.2)。为了使它更容易,我们决定开始使用具有所有可用功能的通用主机,用于所有未来的增强(appsettings/logging/DI 等)。这个想法是,一旦我们切换到 API,我们将复制主机配置,并且大部分代码将继续工作。我们将 host.Services.GetService() 暴露给遗留代码库,它将用作简单的 ServiceLocator。

问题是:使用这种方法有什么缺点吗?另外,我们需要在通用主机上使用 Start/Stop/Run 吗?根据我所阅读的内容,这仅在运行我们不打算使用的 IHostedService 时需要。我测试了它是否在不调用 Start/Stop 的情况下工作,一切似乎都很好,但我发现的所有示例都调用了 Start/Stop,即使没有 IHostedService。

        private IHost _host;
        private PricingHost()
        {
            _host = new HostBuilder()
                .UseEnvironment("BETA")
                .ConfigureAppConfiguration((hostContext, configApp) =>
                {
                    configApp.SetBasePath(System.IO.Directory.GetCurrentDirectory());
                    configApp.AddJsonFile("appsettings.json", optional: true);
                    configApp.AddJsonFile(
                        $"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",
                        optional: true);
                })
                .ConfigureLogging((hostContext, configLogging) =>
                {
                    configLogging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
                    configLogging.AddDebug();
                    configLogging.AddConsole();
                    configLogging.AddCustomLogger();
                })
                .ConfigureServices((hostContext, configServices) =>
                {
                    var startup = new Startup(hostContext.Configuration);
                    startup.ConfigureServices(configServices);
                })
                .Build();
        }

        public T GetService<T>()
        {
            return _host.Services.GetService<T>();
        }

        public T GetRequiredService<T>()
        {
            return _host.Services.GetRequiredService<T>();
        }

更新:让我试着澄清一下。我们有 10 年历史的传统 Windows 服务。我们计划在未来某个时候将其转换为 asp.net .net core api,但目前仍在进行 Windows 服务的工作。为了简化未来的过渡,我们希望将通用主机用于 DI、配置访问、日志记录等。我们只是想将其用作服务定位器(因此不需要 IHostedService),但在未来一段时间内能够只需复制所有配置/设置并将其粘贴到新 API 中

在进行小型 POC 之后,我知道这完全有可能,但我想弄清楚是否有任何我应该注意的陷阱以及如何在没有 IHostedService 的情况下正确使用 GenericHost(我是否需要调用 Run/Start/Stop在某些时候或没有必要)?

更新:添加我在这里所做的点网小提琴示例

标签: asp.net-core.net-core

解决方案


好的,从我的评论和您的回答中,我将尝试找到答案:)

我认为如果你真的需要使用服务定位器模式而不是依赖注入,你也可以在依赖注入中注册IServiceProvider它本身

第一步:注册服务提供者(IServiceProvider

private static IHostBuilder CreateHostBuilder()
{
    // One host for all services
    return new HostBuilder()
        .UseEnvironment("BETA")
        .ConfigureAppConfiguration((hostContext, configApp) =>
        {
            // your configuration
            configApp.AddJsonFile("appsettings.json", optional: true);
        })
        .ConfigureLogging((hostContext, configLogging) =>
        {
            configLogging.AddConsole();
        })
        .ConfigureServices((hostContext, configServices) =>
        {
            // your services
            configServices.AddHostedService<PricingHost>();

            // register the service provider as singleton
            configServices.AddSingleton<IServiceProvider>(sp => sp);
        });
}

第二步:使用IHostedService

就像您说的那样,您不想使用IHostedService,但据我所知,您想实例化多个IHost类,这最终非常不方便。所以我认为最好的解决方案是在-methodsIHostedServices中使用旧项目中的代码来实现它,并让它们使用循环或 a来“永远”运行并使用as 服务定位器。StartAsync(...)whileTask.Delay(forever)IServiceProvider

// Each service as IHostedService
public class PricingHost : IHostedService 
{
    // Usage as: Service Locator
    private readonly IServiceProvider _serviceProvider;

    public PricingHost(IServiceProvider sp)
    {
        _serviceProvider = sp;  
    }

    public async Task StartAsync(CancellationToken token)
    {
        Task.Run(() => RunningTask(token));

        await Task.Delay(Timeout.Infinite, token);
    }

    public Task StopAsync(CancellationToken token)
    {
        return Task.CompletedTask;
    }

    private Task RunningTask(CancellationToken token)
    {
        // do your things ...
        var logger = _serviceProvider.GetRequiredService<ILogger<PricingHost>>();
        logger.LogInformation("Working...");

        return Task.CompletedTask;
    }
}

如果您查看我在 dotnet fiddle 上的简短示例,可能会更清楚

如果有任何不清楚的地方,请给我留言。也许我没有弄明白为什么你想在你的应用程序中有多个主机。


推荐阅读