C# 与.NET Core 2.x中HostBuilder的简单注入器集成

C# 与.NET Core 2.x中HostBuilder的简单注入器集成,c#,.net-core,simple-injector,microsoft.extensions.hosting,C#,.net Core,Simple Injector,Microsoft.extensions.hosting,我们正在开发一个运行.NETCore2.x的windows服务。在Steve Gordon的这篇博文之后,事情似乎运行得很好。。。只要我们使用IServiceCollection。我更喜欢SimpleInjector,但我不确定如何像在asp.net core中那样使用它。我知道有一种方法可以替换这里描述的内置DI,我知道SI团队不推荐这种方法,所以在这个用例中有更好的方法吗 这是我到目前为止所拥有的,但它让人不舒服 --主程序 internal class Program { priv

我们正在开发一个运行.NETCore2.x的windows服务。在Steve Gordon的这篇博文之后,事情似乎运行得很好。。。只要我们使用
IServiceCollection
。我更喜欢SimpleInjector,但我不确定如何像在asp.net core中那样使用它。我知道有一种方法可以替换这里描述的内置DI,我知道SI团队不推荐这种方法,所以在这个用例中有更好的方法吗

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

--主程序

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();
        }
    }
}
内部类程序
{
专用静态异步任务主(字符串[]args)
{
var isService=!(Debugger.IsAttached | | args.Contains(“--console”);
var builder=新主机生成器()
.ConfigureServices((主机上下文,服务)=>
{
services.AddHostedService();
//在这里配置SimpleInjector???
});
如果(iService)
{
等待builder.RunAsServiceAsync();
}
其他的
{
等待builder.RunConsoleAsync();
}
}
}

在这里配置容器或多或少会起作用,但主机创建的第一个类(即本例中的
Runner
)由主机创建,并通过IServiceCollection注入任何依赖项。所以我的问题是如何让它从我的SI容器中注入?

这里显而易见的答案是。。。没有任何依赖项注入到
运行程序中。相反,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;
    }
}

我将钩住HostBuilder的ConfigureContainer方法,并在那里设置SimpleInjector,如下所示:

                   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();
        }
这与simpleinjector文档中关于使用ASP.NET Core设置容器的说明一致:

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


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

通用主机从服务集合解析托管服务,因此解决方案是在简单注入器中注册托管服务,然后从简单注入器解析托管服务以在服务集合中注册:

                   HostBuilder()
                   .ConfigureServices((hostContext, services) =>
                   {
                       services.AddLogging();
                       services.AddOptions();
                   })
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();
var container=newcontainer();
var host=new HostBuilder()
//...
.ConfigureServices((上下文、服务)=>
{
container.Collection.Append(typeof(IHostedService),typeof(Runner));
services.AddSingleton(=>container.GetAllInstances());
})
//...
.Build();
container.Verify();
等待host.RunAsync();

我不确定我是否理解了您的问题以及您遇到了什么问题。在将Simple Injector与ASP.NET Core集成时,您永远不会替换内置容器,因此不应该使用作为服务运行的东西。因此,您将遵循一种相同的方法,即通过替换框架的一个主要拦截点(如
IControllerActivator
)来插入框架。那么,是什么阻止你采取这种做法呢?@Steven是的,我同意这个问题有点含糊不清,我希望Steven Gordon的博客文章能把它说清楚。我已经编辑了这个问题,并将把现在显而易见的答案发布在minute@Steven此外,此处的主机生成器没有IControllerActiviator,因为我没有使用ASP.NET core.:)但是你做得比我好吗?一个请求进来了,那会发生什么?@Steven啊,“请求”实际上是rebus消息。因此,我的引导程序配置服务总线(rebus),该总线进行轮询,并根据需要使用SI适配器建立消息处理程序依赖关系。这难道不意味着运行程序现在是不可测试的吗?