如何注入对特定IHostedService实现的引用?

Mic*_*pat 12 dependency-injection asp.net-core

我的网络应用程序有一个后台服务,可以收听服务总线.基于文档,看起来运行后台服务的内置方式是实现IHostedService.

所以我有一些看起来像这样的代码:

public class ServiceBusListener : IMessageSource<string>, IHostedService
{
    public virtual event ServiceBusMessageHandler<string> OnMessage = delegate { };

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // run the background task...
    }

    // ... other stuff ...
}
Run Code Online (Sandbox Code Playgroud)

然后注册该服务Startup.cs:

services.AddSingleton<IHostedService, ServiceBusListener>();
Run Code Online (Sandbox Code Playgroud)

一旦我更新到ASP.NET 2.1,我可以使用新的便捷方法:

services.AddHostedService<ServiceBusListener>();
Run Code Online (Sandbox Code Playgroud)

但我相信这两者在功能上是等价的.

复杂性:我的Web应用程序有多个实现IHostedService(特别是服务总线监听器的不同实例).

问题:如何让其他组件获得对特定托管服务实现(我的服务总线监听器)的引用?换句话说,如何将特定实例注入组件?

用例:我的后台服务侦听服务总线消息,然后重新发布消息作为.NET事件(如果您想知道,消费代码处理线程问题).如果事件在后台服务上,则订阅者需要获得对后台服务的引用才能进行订阅.

我尝试过:如果我做了明显的事情,并声明ServiceBusListener作为依赖注入到不同的组件,我的启动代码抛出"无法解析类型的服务"异常.

甚至可以请求特定的实现IHostedService?如果没有,最好的解决方法是什么?介绍我的服务和消费者可以参考的第三个组件?避免IHostedService并手动运行后台服务?

Mic*_*pat 19

事实证明这是一个简单的方法(感谢指针Steven).

如果您需要能够注入/获取某些服务的引用,请继续正常注册服务(无需担心任何IHostedService内容):

services.AddSingleton<ServiceBusListener>();
Run Code Online (Sandbox Code Playgroud)

现在我们可以注册一个单独的托管服务,其唯一的职责是启动/停止我们刚刚注册的服务:

services.AddHostedService<BackgroundServiceStarter<ServiceBusListener>>();
Run Code Online (Sandbox Code Playgroud)

BackgroundServiceStarter辅助类在哪里看起来像:

public class BackgroundServiceStarter<T> : IHostedService where T:IHostedService
{
    readonly T backgroundService;

    public BackgroundServiceStarter(T backgroundService)
    {
        this.backgroundService = backgroundService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        return backgroundService.StartAsync(cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return backgroundService.StopAsync(cancellationToken);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新2018/8/6:由于ygoe的建议,更新了代码以避免服务定位器模式


MDR*_*MDR 9

.net core 现在具有AddHostedService重载,可让您指定用于解析托管服务的工厂函数/方法。这使您可以将托管服务指定为单例,然后在必要时注入它。

文档称它从 .Net Core 3.x 起就可用

可以说,人们应该分析导致这条路径的架构选择,以确保这条路径确实是需要的。但有时可能是必要的。

AddHostedService<THostedService>(IServiceCollection, Func<IServiceProvider,THostedService>)
Run Code Online (Sandbox Code Playgroud)

用法是

services
   .AddSingleton<IMyServiceContract, MyServiceImplementation>()
   .AddHostedService(services => (MyServiceImplementation) services.GetService<IMyServiceContract>())
;
Run Code Online (Sandbox Code Playgroud)

如果您的 IServiceContract 继承自 IHostedService 或者您不使用接口,则不需要进行强制转换。


ygo*_*goe 7

谢谢您的回答,迈克尔,效果很好。奇怪的是我们需要这样的技巧才能使依赖注入真正起作用。

我更改了班级,因此您不需要服务定位器。泛型在这里有帮助。

public class BackgroundServiceStarter<T> : IHostedService
    where T : IHostedService
{
    private readonly T backgroundService;

    public BackgroundServiceStarter(T backgroundService)
    {
        this.backgroundService = backgroundService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        return backgroundService.StartAsync(cancellationToken);
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return backgroundService.StopAsync(cancellationToken);
    }
}
Run Code Online (Sandbox Code Playgroud)


sro*_*oll 6

根据您的回答,我提出了一种有用的扩展方法。它允许向IHostedService另一个接口注册一个。

另一个接口不需要实现IHostedService,因此您无需公开StartAsync()StopAsync()方法。

public static class ServiceCollectionUtils
{
    public static void AddHostedService<TService, TImplementation>(this IServiceCollection services)
        where TService : class
        where TImplementation : class, IHostedService, TService
    {
        services.AddSingleton<TService, TImplementation>();
        services.AddHostedService<HostedServiceWrapper<TService>>();
    }

    private class HostedServiceWrapper<TService> : IHostedService
    {
        private readonly IHostedService _hostedService;

        public HostedServiceWrapper(TService hostedService)
        {
            _hostedService = (IHostedService)hostedService;
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            return _hostedService.StartAsync(cancellationToken);
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return _hostedService.StopAsync(cancellationToken);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


jjx*_*tra 5

从 .net core 3.1 开始,其他答案中的复杂性都不再需要。如果您不需要从另一个类获取对您的类的具体引用,只需调用:

services.AddHostedService<MyHostedServiceType>();
Run Code Online (Sandbox Code Playgroud)

如果您必须有具体的参考,请执行以下操作:

services.AddSingleton<IHostedService, MyHostedServiceType>();
Run Code Online (Sandbox Code Playgroud)