使用ASP.NET Core 2.0将简单的Injector组件注入IHostedService [英] Injecting Simple Injector components into IHostedService with ASP.NET Core 2.0

查看:76
本文介绍了使用ASP.NET Core 2.0将简单的Injector组件注入IHostedService的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在ASP.NET Core 2.0中,有一种方法可以通过实现IHostedService接口来添加后台任务(请参见

In ASP.NET Core 2.0, there is a way to add background tasks by implementing the IHostedService interface (see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/hosted-services?view=aspnetcore-2.0). By following this tutorial, the way I was able to get it working was by registering it in the ASP.NET Core container. My goal is to be reading messages from a queue and processing the jobs in the background; a message is posted to the queue (via controller action) and then processed in the background on a timed interval.

// Not registered in SimpleInjector
services.AddSingleton<IHostedService, MyTimedService>(); 

当我将此注册放入ASP.NET Core容器时,它会在应用程序启动时自动启动该过程.但是,当我在SimpleInjector中注册该服务时,该服务不会自动启动.我相信是这样,因为我们只向MvcControllers和MvcViewComponents注册了SimpleInjector容器:

When I put this registration in the ASP.NET Core container, it kicks off the process automatically on application startup. However, when I register this in SimpleInjector, the service is not automatically started. I believe this is the case because we only register the SimpleInjector container with the MvcControllers and MvcViewComponents:

// Wire up simple injector to the MVC components
container.RegisterMvcControllers(app);
container.RegisterMvcViewComponents(app);

我遇到的问题是,当我想开始将来自SimpleInjector的组件寄存器(例如,存储库,带有修饰符的通用处理程序...)注入到IHostedService的实现中时,如下所示:

The problem I run into is when I want to start injecting components register from SimpleInjector (e.g. Repositories, generic handlers with decorators...) into an implementation of IHostedService as demonstrated below:

public class TimedService : IHostedService, IDisposable
{
    private IJobRepository _repo;
    private Timer _timer;

    public TimedService(IJobRepository repo)
    {
        this._repo = repo;
    }
    ...
    ...
    ...
}

由于IHostedService已在ASP.NET Core中注册,而不是在Simple Injector中注册,因此在运行定时后台服务时收到以下错误:

Since IHostedService is registered with ASP.NET Core and not Simple Injector, I receive the following error when running the timed background service:

未处理的异常:System.InvalidOperationException:尝试激活"Optimization.API.BackgroundServices.TimedService"时,无法解析类型为"Optimization.Core.Interfaces.IJobRepository"的服务.

Unhandled Exception: System.InvalidOperationException: Unable to resolve service for type 'Optimization.Core.Interfaces.IJobRepository' while attempting to activate 'Optimization.API.BackgroundServices.TimedService'.

所以我的问题是,在Simple Injector中实现后台任务的最佳方法是什么?与标准MVC集成相比,这是否需要单独的集成包?如何将我的简单注入器注册注入到IHostedService中?如果我们可以在Simple Injector中注册后自动启动该服务,那么我认为可以解决此问题.

So my question is, what is the best way to implement background tasks in Simple Injector? Does this require a separate integration package than the standard MVC integration? How am I able to inject my Simple Injector registrations into the IHostedService? If we could automatically start the service after being registered in Simple Injector, I think that would solve this problem.

感谢您在此提出的任何建议以及有关此主题的任何建议!我可能做错了.在过去的一年中,我真的很喜欢使用Simple Injector.

Thank you for any pointers here and for any advice on this topic! I could be doing something wrong. I have really enjoyed using Simple Injector for the past year.

推荐答案

有多种方法可以解决此问题.最简单的方法可能是交叉链接托管服务,以使内置配置系统从Simple Injector解析托管服务:

There are multiple ways to approach this. The simplest way is probably to cross-wire the hosted service in such way that the built-in configuration system resolves the hosted service from Simple Injector:

// Register in Simple Injector as Singleton
container.RegisterSingleton<THostedService>();

// Cross-wire TimedService in the built-in configuration system
services.AddSingleton<IHostedService>(
    c => container.GetInstance<TimedService>());

请注意,托管服务仅被解析一次,并被永久缓存,有效地使它们成为Singletons.这就是为什么您应该在Simple Injector中将其注册为Singleton的原因.

Do note that hosted services are resolved just once, and cached for ever, effectively making them Singletons. This is why you should register it in Simple Injector as Singleton.

但是,这样做的结果是,您将无法将任何ScopedTransient依赖项注入到托管服务中.最重要的是,它迫使您让您的应用程序组件(TimedService)依赖于ASP.NET Core抽象(IHostedService).这不理想.

Concequence of this, however, is that you won't be able to inject any Scoped or Transient dependencies into your hosted service. On top of that, it forces you to let your application component (TimedService) have a dependency on an ASP.NET Core abstraction (IHostedService). This is not ideal.

因此,我的首选方法是创建一个适配器实现,该实现在ASP.NET Core配置系统中注册,该适配器实现将调用转移到Simple Injector,同时使用特定于应用程序的抽象来实现您的服务.因此,您无需创建许多IHostedService实现,而是定义了一种特定于您的应用程序且非常理想的抽象.我们将此抽象称为IMyJob.

My preferred approach, therefore, is to instead create an adapter implementation that you register with the ASP.NET Core configuration system that forwards the calls to Simple Injector while using an application-specific abstraction to implement your service. So instead creating many IHostedService implementations, you define an abstraction that is specific and ideal to your application. Let's call this abstraction IMyJob.

IHostedService适配器实现可能如下所示:

The IHostedService adapter implementation might look like this:

public class SimpleInjectorJobProcessorHostedService : IHostedService, IDisposable
{
    private readonly Container container;
    private Timer timer;

    public SimpleInjectorJobProcessorHostedService(Container c) => this.container = c;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        this.timer = new Timer(this.DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        // Run operation in a scope
        using (AsyncScopedLifestyle.BeginScope(this.container))
        {
            // Resolve the collection of IMyJob implementations
            foreach (var service in this.container.GetAllInstances<IMyJob>())
            {
                service.DoWork();
            }
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        this.timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public void Dispose() => this.timer?.Dispose();
}

您可以按以下步骤在ASP.NET核心中注册它:

You can register it in ASP.NET core as follows:

services.AddSingleton<IHostedService>(
    new SimpleInjectorJobProcessorHostedService(container)); 

这样,您运行的实际作业就可以忽略ASP.NET Core,并且可以定义如下:

This way the actual jobs that you run can stay oblivious to ASP.NET Core and can be defined as follows:

public class CoolJob : IMyJob
{
    private readonly IJobRepository repo;

    public CoolJob(IJobRepository repo) => this.repo = repo;

    public void DoWork() => ...
}

所有作业都可以在Simple Injector中进行注册,如下所示:

And all jobs can be registered in Simple Injector as follows:

 // NOTE: Simple Injector v4.3 API
container.Collection.Register<IMyJob>(typeof(CoolJob).Assembly);

这篇关于使用ASP.NET Core 2.0将简单的Injector组件注入IHostedService的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆