如何将依赖项注入 .Net Core 项目中的自定义 WebHostService?

Nec*_*ras 5 c# dependency-injection simple-injector asp.net-core

我试图创建所描述的,这将作为Windows服务运行的服务在这里。我的问题是示例 Web 主机服务构造函数只接受一个IWebHost参数。我的服务需要一个更像这样的构造函数:

public static class HostExtensions
{
    public static void RunAsMyService(this IWebHost host)
    {
        var webHostService =
            new MyService(host, loggerFactory, myClientFactory, schedulerProvider);
        ServiceBase.Run(webHostService);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的Startup.cs文件看起来类似于:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .AddEnvironmentVariables()
        .AddInMemoryCollection();

    this.Configuration = builder.Build();
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddOptions();
    this.container.RegisterSingleton<IConfiguration>(this.Configuration);
    services.AddSingleton<IControllerActivator>(
        new SimpleInjectorControllerActivator(container));
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    app.UseSimpleInjectorAspNetRequestScoping(this.container);

    this.container.Options.DefaultScopedLifestyle = new AspNetRequestLifestyle();

    this.InitializeContainer(app, loggerFactory);
    this.container.Verify();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
}

private void InitializeContainer(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
    container.Register(() => loggerFactory, Lifestyle.Singleton);
    container.Register<IMyClientFactory>(() => new MyClientFactory());
    container.Register<ISchedulerProvider>(() => new SchedulerProvider());
}
Run Code Online (Sandbox Code Playgroud)

显然,我使用 Simple Injector 作为 DI 容器。它IServiceCollection他们的文档中详细注册。

我的问题是如何访问 HostExtensions 类中的框架容器(IServicesCollection),以便将必要的依赖项注入到MyService? 对于 MVC 控制器,这一切都只是在幕后处理,但我不知道任何详细说明如何在其他地方需要的地方访问它的文档。

Ste*_*ven 3

您只需对代码进行一些小的调整即可使其正常工作。

1.在类中创建aContainer字段:public staticStartup

public class Startup
{
    public static readonly Container container = new Container();
Run Code Online (Sandbox Code Playgroud)

2. 将验证移出Startup并移入Main

Startup这样做允许在类完成之后但在应用程序实际启动之前将额外的注册添加到容器中:

public static void Main(string[] args)
{
    ...
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(directoryPath)
        .UseStartup<Startup>()
        .Build();

    // Don't forget to remove the Verify() call from within the Startup.
    Startup.Container.Verify();

    ...
}
Run Code Online (Sandbox Code Playgroud)

3. 将您注册MyService为单身人士

在容器中将其显式注册为单例允许简单注入器对其运行诊断,并防止MyService可能意外拥有的意外强制依赖项。您应该将其注册为单例,因为它将在应用程序期间保持活动状态:

public static void Main(string[] args)
{
    ...

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(directoryPath)
        .UseStartup<Startup>()
        .Build();

    Startup.Container.RegisterSingleton<MyService>();

    Startup.Container.Verify();

    ...
}
Run Code Online (Sandbox Code Playgroud)

3. 注册MyService所需的缺失依赖项。

MyService依赖于host、loggerFactory、myClientFactory和schedulerProvider,目前这些都没有注册。

可以在方法host内注册Main

public static void Main(string[] args)
{
    ...
    Startup.Container.RegisterSingleton<MyService>();
    Startup.Container.RegisterSingleton<IWebHost>(host);

    Startup.Container.Verify();

    ...
}
Run Code Online (Sandbox Code Playgroud)

虽然loggerFactory可以在类中注册Startup

public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    Container.RegisterSingleton(loggerFactory);

    ...
}
Run Code Online (Sandbox Code Playgroud)

我假设myClientFactoryschedulerProvider依赖项已经在容器中注册。

4. 将RunAsMyService扩展方法替换为容器中的简单解析

由于MyService已成功在容器中注册及其所有依赖项,我们现在应该能够从容器中解析它并将其传递给方法ServiceBase.Run

public static void Main(string[] args)
{
    ...

    Startup.Container.Verify();

    ServiceBase.Run(Startup.Container.GetInstance<MyService>());
}
Run Code Online (Sandbox Code Playgroud)

这应该就是全部了。

最后一点,根据您正在构建的应用程序的类型,您可能Startup根本不需要如此复杂的类。您可以将容器的配置移至更靠近方法的位置Main。您是否应该这样做,取决于您实际需要多少。

这是一个不上课的例子Startup

public static void Main(string[] args)
{
    var container = new Container();
    container.Options.DefaultScopedLifestyle = new AspNetRequestLifestyle();

    IWebHost host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .ConfigureServices(services =>
        {
            // Configure framework components
            services.AddOptions();
        })
        .Configure(app =>
        {
            app.UseSimpleInjectorAspNetRequestScoping(container);

            // Apply cross-wirings:
            container.RegisterSingleton(
                app.ApplicationServices.GetRequiredService<ILoggerFactory>());
        })
        .UseStartup<Startup>()
        .Build();

    container.RegisterSingleton<MyService>();
    container.RegisterSingleton(host);

    container.Verify();

    ServiceBase.Run(Startup.Container.GetInstance<MyService>());
}
Run Code Online (Sandbox Code Playgroud)

  • 我确信你不会遇到同样的问题,因为那样的话你肯定知道容器是什么;这个问题是关于简单注射器的。 (2认同)