【问题标题】:How do I get a reference to an IHostedService via Dependency Injection in ASP.NET Core?如何通过 ASP.NET Core 中的依赖注入获取对 IHostedService 的引用?
【发布时间】:2019-02-01 20:31:38
【问题描述】:

详情

我尝试在 ASP.NET 2.1 中使用推荐的IHostedService 接口创建后台处理结构。我注册服务如下:

services.AddSingleton<AbstractProcessQueue<AbstractImportProcess>>();
services.AddHostedService<AbstractBackgroundProcessService<AbstractImportProcess>>();

services.AddSignalR();

AbstractProcessQueue 只是对可以入队和出队的进程BlockingCollection 的包装。 AbstractBackgroundProcessService 实现了IHostedService 接口,并在队列中查找它可以启动的新进程。

现在,当我在SignalR 集线器内尝试通过Dependency Injection 机制获取对后台处理服务的引用时,麻烦就开始了。我尝试了以下解决方案,但似乎都没有按预期工作:

选项 1:

public HubImportClient(IServiceProvider provider)
{
    //This returns null.
    var service = provider.GetService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}

选项 2:

public HubImportClient(IServiceProvider provider)
{
    //This returns null.
    var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>>));
}

选项 3:

public HubImportClient(IServiceProvider provider)
{
    //This throws an exception, because the service is missing.
    var service = provider.GetRequiredService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}

选项 4:

public HubImportClient(IServiceProvider provider)
{
    //This throws an exception, because the service is missing.
    var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetRequiredService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>);
}

选项 5:

public HubImportClient(IServiceProvider provider)
{
    //This returns a correct service, but prevents me from adding additional AbstractBackgroundProcessService implementations with different type parameters.
    //Additionally, it seems like this reference was newly created, and not the instance that was created on application startup (i.e. the hash codes are different, and the constructor is called an additional time).
    var service = provider.GetService<IHostedService>();
    if(service is AbstractBackgroundProcessService<AbstractProcessService>)
    {    this.Service = (AbstractBackgroundProcessService<AbstractProcessService>) service;}
}

选项 6:

public HubImportClient(IServiceProvider provider)
{
    //This works similarly to the previous option, and allows multiple implementations, but the constructor is still called twice and the instances thus differ.
    AbstractBackgroundProcessService<AbstractImportProcess> service = null;
    foreach(IHostedService service in provider.GetServices<IHostedService>())
    {
        if(service is AbstractBackgroundProcessService<AbstractImportProcess>)
        {
            service = (AbstractBackgroundProcessService<AbstractImportProcess>) service;
            break;
        }
    }  
}

选项 7:

public HubImportClient(IServiceProvider provider)
{
    //This just skips the for each loop all together, because no such services could be found.
    AbstractBackgroundProcessService<AbstractImportProcess> service = null;
    foreach(AbstractBackgroundProcessService<AbstractImportProcess> current in provider.GetServices<AbstractBackgroundProcessService<AbstractImportProcess> >())
    {
        service = current;
        break;
    }    
}

选项 8:

//This works, but prevents multiple implementations again.
public HubImportClient(IHostedService service)
{
    this.Service = service;   
}

选项 9:

//This does not work again.
public HubImportClient(AbstractBackgroundProcessService<AbstractImportProcess> service)
{
    this.Service = service;   
}

问题

那么我的问题仍然存在:我应该如何获得对 IHostedService 实现的引用,以便:

(a):我可以注入服务的多个实例,它们的区别仅在于它们的类型参数(例如,AbstractImportProcesses 的托管服务以及 AbstractExportProcesses 的托管服务)

(b):对于该特定类型参数,IHostedService 只有一个实例。

提前感谢您的帮助!

【问题讨论】:

  • 为什么直接需要服务实例?这是非典型的。您需要做的只是注册IHostedService 实现。 ASP.NET Core 负责实例化和运行它。
  • @ChrisPratt 我希望获得我的 HostedService 的已配置活动实例的状态,这将是启动/停止的全部目的,我的作用域服务接收由我的 HostedService 引导的热实例.
  • 最后我只是向我的 HostedService 添加了一个静态属性,以便在 DI 中注册。

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


【解决方案1】:

来自提到的git page的当前解决方法:

services.AddSingleton<YourServiceType>();
services.AddSingleton<IHostedService>(p => p.GetService<YourServiceType>());

这会将您的服务创建为托管服务(在主机启动和关闭时运行和停止),并在您需要的任何地方作为依赖注入。

更新:

我不再将此解决方案视为“解决方法”。

相反,我会这样描述:托管组件和常规服务是不同类型的实体,每个实体都有自己的用途。

然而,上面的解决方案允许将它们组合起来,将托管组件注册为服务,可以在依赖解析链中使用。

这太棒了。

【讨论】:

  • dotnet 5 中有没有更好的方法?
  • 不确定它是如何工作的,但是当我运行 _serviceProvider.GetService();我找回了我的托管服务类型。不确定它将如何与多个托管服务一起使用。
  • @too 不,而且我认为您无法证明某些单线会因任何原因“更好”,除非是单线。
  • 谢谢@AgentFire ;) 拯救了我的一天
【解决方案2】:

围绕这个话题进行了一些讨论。例如,请参阅:https://github.com/aspnet/Hosting/issues/1489。您将遇到的问题之一是托管服务被添加为临时服务(来自 ASP.NET Core 2.1+),这意味着从依赖注入容器解析托管服务每次都会产生一个新实例。

一般建议是将您希望与其他服务共享或与其他服务交互的任何业务逻辑封装到特定服务中。查看您的代码,我建议您在 AbstractProcessQueue&lt;AbstractImportProcess&gt; 类中实现业务逻辑,并使执行业务逻辑成为 AbstractBackgroundProcessService&lt;T&gt; 的唯一关注点。

【讨论】:

  • 感谢您的回答。您的建议与 Chris Pratt 的评论相结合,使我决定将业务逻辑移至流程队列并保留托管服务的原样。我不太明白他们为什么选择将托管服务添加为临时依赖项,但这个解决方案现在就可以了。非常感谢您的帮助!
  • 那边那个贴子真奇怪,希望在座的大家都能看懂。
【解决方案3】:

这只是@AgentFire 对答案的轻微修改。这种方法更清晰,并且允许在单个 Web 服务中托管多个后台服务。

services.AddSingleton<YourServiceType>();
services.AddHostedService<YourServiceType>(p => p.GetRequiredService<YourServiceType>());

【讨论】:

    【解决方案4】:

    在 .Net Core 3.1 和 .Net 5.0 中,您可以通过以下方式获取对托管服务的现有实例的引用:
    IEnumerable&lt;IHostedService&gt; allHostedServices = this.serviceProvider.GetService&lt;IEnumerable&lt;IHostedService&gt;&gt;();

    【讨论】:

      猜你喜欢
      • 2018-04-05
      • 2018-01-16
      • 2019-02-22
      • 1970-01-01
      • 1970-01-01
      • 2017-09-10
      • 1970-01-01
      • 2020-08-21
      • 2017-10-26
      相关资源
      最近更新 更多