【问题标题】:How do I inject dependencies into a custom WebHostService in a .Net Core project?如何将依赖项注入 .Net Core 项目中的自定义 WebHostService?
【发布时间】:2016-12-17 19:11:34
【问题描述】:

我正在尝试创建一个服务,该服务将作为 Windows 服务运行,如 here 所述。我的问题是示例 Web 主机服务构造函数仅采用 IWebHost 参数。我的服务需要一个更像这样的构造函数:

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

我的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());
}

显然,我使用 Simple Injector 作为 DI 容器。它在IServiceCollection 注册,详见their documentation

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

【问题讨论】:

  • 我很好奇,当 .Net Core 附带的一个 (IServiceCollection) 工作正常时,为什么还要使用另一个 DI 容器?这似乎添加了一个完全没有必要的层。有什么优势吗?
  • @R.Richards 内置容器不太适合构建围绕 SOLID 原则构建的大型应用程序,正如 here 所解释的那样。

标签: c# dependency-injection asp.net-core simple-injector


【解决方案1】:

您只需要对您的代码进行几个小的 tweeks 即可使其正常工作。

1。在Startup 类中将Container 设为public static 字段:

public class Startup
{
    public static readonly Container container = new Container();

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();

    ...
}

3。将您的 MyService 注册为单身人士

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

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

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

    Startup.Container.RegisterSingleton<MyService>();

    Startup.Container.Verify();

    ...
}

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();

    ...
}

虽然loggerFactory 可以在Startup 类中注册:

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

    ...
}

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

4。将 RunAsMyService 扩展方法替换为来自容器的简单解析

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

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

    Startup.Container.Verify();

    ServiceBase.Run(Startup.Container.GetInstance<MyService>());
}

应该就是这样了。

最后一点,根据您正在构建的应用程序的类型,您可能根本不需要如此详尽的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>());
}

【讨论】:

  • 我也遇到了同样的问题,能否请您详细说明一下容器是什么?
  • 我确定您没有遇到同样的问题,因为在这种情况下您肯定会知道容器是什么;这个问题是关于简单注射器的。
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 2020-12-20
  • 1970-01-01
  • 2017-01-09
  • 2018-12-02
  • 1970-01-01
  • 2016-11-26
  • 1970-01-01
相关资源
最近更新 更多