2

我们正在开发一个运行 .net core 2.x 的 Windows 服务。在 Steve Gordon 的这篇博客文章之后,将 .netcore 通用主机应用程序作为服务运行,事情似乎运行良好……只要我们使用IServiceCollection. 我更喜欢 SimpleInjector,但我不确定如何像在 asp.net 核心中那样使用它。我有一种方法可以替换内置 DI,如此处所述默认服务容器替换,我知道 SI 团队不推荐ASP.NET Core MVC 集成指南的方法,所以在这个用例中有更好的方法吗?

这是我到目前为止所拥有的,但它不舒服

--主程序

internal class Program
{
    private static async Task Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        var builder = new HostBuilder()
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Runner>();
                //configure SimpleInjector here???
            });

        if (isService)
        {
            await builder.RunAsServiceAsync();
        }
        else
        {
            await builder.RunConsoleAsync();
        }
    }
}

在此处配置容器或多或少可以工作,但由主机创建的第一个类(即Runner在这种情况下)由主机创建并通过 IServicesCollection 注入任何依赖项。所以我的问题是如何让它从我的 SI 容器中注入?

4

3 回答 3

1

这里明显的答案是......不要将任何依赖项注入Runner. 相反,Runner 是代表您的应用程序入口点的类,因此我在那里配置我的容器并在 Runner 停止时将其处理掉。跑步者的完整代码...

public class Runner : IHostedService, IDisposable
{
    private Container _container;
    public Runner()
    {
        _container = new Container();
        _container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Bootstrapper.Bootstrap(_container);
        return Task.CompletedTask;
    }

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

    public void Dispose()
    {
        _container.Dispose();
        _container = null;
    }
}
于 2018-09-20T12:33:07.970 回答
0

我会挂接到 HostBuilder 的 ConfigureContainer 方法并在那里设置 simpleinjectore,如下所示:

                   HostBuilder()
                   .ConfigureContainer<ServiceCollection>((builder, services) =>
                   {
                       var container = new Container();

                       container.RegisterSingleton<IJobRepository, JobRepository>();
                       services.AddTransient<IHostedService, TimedService>();

                   })
                   .ConfigureServices((hostContext, services) =>
                   {
                       // Originally we would have done this
                       //services.AddHostedService<Service>();
                   })
                   .Build();

        using (host)
        {
            await host.StartAsync();
            await host.WaitForShutdownAsync();
        }

虽然您确实可以使用您的 IHostedService 实现,但我认为它可能会隐藏正在发生的事情。我认为基础设施引导应该在一个地方完成,或者至少在一个地方进行编排。我认为容器是基础设施,并将通过 HostBuilder 方法将其与应用程序的其余部分一起设置。

另一个好处可能是您不会完全替换 ServiceCollection,因为它可以很好地处理其他与框架相关的事情。我仍然会使用 ServiceCollection 做的一些事情的示例:

                   HostBuilder()
                   .ConfigureServices((hostContext, services) =>
                   {
                       services.AddLogging();
                       services.AddOptions();
                   })

这与 simpleinjector 文档中关于使用 ASP.NET Core 设置容器的说明一致:

Simple Injector 的实践是使用 Simple Injector 构建应用程序组件的对象图,并让内置容器构建框架和第三方组件,Simple Injector 的实践是使用 Simple Injector 构建应用程序的对象图您的应用程序组件并让内置容器构建框架和第三方组件

这同样适用于 .net 核心和通用 HostBuilder。

于 2019-05-02T20:33:22.050 回答
0

通用主机从服务集合中解析托管服务,因此解决方案是在 Simple Injector 中注册托管服务,然后从 Simple Injector 中解析它们以在 Services 集合中注册:

var container = new Container();
var host = new HostBuilder()
//...
    .ConfigureServices((context, services) =>
    {
        container.Collection.Append(typeof(IHostedService), typeof(Runner));
        services.AddSingleton(_ => container.GetAllInstances<IHostedService>());
    })
//...
    .Build();

container.Verify();
await host.RunAsync();
于 2019-10-18T04:55:59.830 回答