【问题标题】:How to properly use .Net Core Generic Host without IHostedService?如何在没有 IHostedService 的情况下正确使用 .Net Core Generic Host?
【发布时间】:2020-04-14 23:08:20
【问题描述】:

我们有旧版应用程序,我们计划在未来某个时间过渡到 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/在某个时候开始/停止,还是没有必要)?

更新:添加我正在做的 dot net fiddle 示例here

【问题讨论】:

  • 对不起,我想我没有得到你的问题。您想使用通用主机将 API 从 2.2 迁移到 3.1,对吗? Have you read this documentation。您不一定要使用IHostedService,但我认为在其他所有情况下,使用 API 是完全有意义的 :)
  • @Martin 很抱歉不清楚。问题实际上是关于在没有 IHostedService 的情况下使用 GenericHost 的正确方法是什么(我是否需要调用 Run/Start/Stop/etc. 或执行其他任何操作)以及在使用 GenericHost 作为时我应该注意的任何问题ServiceLocator 通过将 host.Services.GetService() 暴露给遗留代码库。我更新了我的问题。

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


【解决方案1】:

好的,根据我的评论和您的回答,我会尝试找到答案:)

我认为如果你真的需要使用服务定位器模式而不是依赖注入,你也可以在依赖注入中注册 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 类,这最终非常不方便。所以我认为最好的解决方案是使用IHostedServices 来实现它,在StartAsync(...)-方法中使用旧项目中的代码,让它们通过while 循环或Task.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;
    }
}

如果你看一下我的短片,也许会更清楚 Example on dotnet fiddle

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

【讨论】:

  • 感谢@Martin 花时间研究这个问题,非常感谢!我觉得我们快到了:) 这就是我现在正在做的事情dotnetfiddle.net/rqThcV 我不需要多个 IHost 实例,所以这就是我将它用作单例的原因(旧应用程序中没有 DI)。我知道最好的方法是启动将启动 ExistingWebService 的 IHostedService 但这只是一个太大的变化,我永远不会得到批准这样做。现有代码不会(不能)以任何方式更改,所以这就是我以这种方式使用 IHost 的原因
  • 使用我的 dot net fiddle 中的示例,一旦我们决定切换到 asp.net core api,我将从 Startup 复制配置 + 复制新建的 Services 等并将其放入 api。我唯一要做的就是调用这些新创建的服务(例如从控制器),大多数新创建的功能将开始工作。这是我想要完成的总体思路
  • 很抱歉坚持使用 IHostedService 的想法,但在您的情况下这是可行的吗:dotnet fiddle? - 你有 1 台主机、多个服务、旧版和 DI :)
  • 有什么特别的原因为什么我们应该以这种方式使用它?我知道这就是它应该使用的方式,但是以我们的方式使用它有什么缺点呢?问题是遗留应用程序同时部署为 Windows 服务和肥皂网络服务,并将其切换到托管服务很可能需要更改 ci/cd 以及 ot 将需要大量回归测试,并且可能会花费很多钱,而我不明白我们将如何从中受益
  • 有缺点。我只是尝试做一个示例,它还可以帮助您更轻松地从 IHost(或旧版 Web 服务)迁移到 IHostedService,并尽可能减少代码更改。我非常了解您的问题在这里也有同样的问题。我们像您一样一步一步地尝试它,然后向 IHostedServices 迈出了更大的一步。这完全取决于您的步骤在逐步场景中的大小:)
猜你喜欢
  • 2017-10-21
  • 1970-01-01
  • 2020-06-09
  • 2019-04-13
  • 2015-04-18
  • 1970-01-01
  • 2017-03-14
  • 2019-06-13
  • 1970-01-01
相关资源
最近更新 更多