在ASP.NET Core 2.0中,有一种方法可以通过实现IHostedService接口(interface)来添加后台任务(请参阅https://docs.microsoft.com/en-us/aspnet/core/fundamentals/hosted-services?view=aspnetcore-2.0)。通过遵循本教程,我能够使其正常工作的方法是将其注册到ASP.NET Core容器中。我的目标是从队列中读取消息并在后台处理作业。一条消息被发布到队列中(通过 Controller 操作),然后在定时间隔内在后台进行处理。

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

当我将此注册放入ASP.NET Core容器时,它会在应用程序启动时自动启动该过程。但是,当我在SimpleInjector中注册该服务时,该服务不会自动启动。我相信是这样,因为我们只向MvcControllers和MvcViewComponents注册了SimpleInjector容器:
// Wire up simple injector to the MVC components
container.RegisterMvcControllers(app);
container.RegisterMvcViewComponents(app);

我遇到的问题是,当我想开始将来自SimpleInjector的组件寄存器(例如存储库,带有装饰器的通用处理程序...)注入(inject)到IHostedService的实现中时,如下所示:
public class TimedService : IHostedService, IDisposable
{
    private IJobRepository _repo;
    private Timer _timer;

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

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



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

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

最佳答案

有多种方法可以解决此问题。最简单的方法可能是交叉链接托管服务,以使内置配置系统从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的原因。

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

因此,我的首选方法是创建一个适配器实现,该实现在ASP.NET Core配置系统中注册,该适配器实现将调用转发到Simple Injector,同时使用特定于应用程序的抽象来实现您的服务。因此,您无需创建许多IHostedService实现,而是定义了一种特定于您的应用程序且非常理想的抽象。我们将此抽象称为IMyJob
IHostedService适配器实现可能如下所示:
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核心中注册它:
services.AddSingleton<IHostedService>(
    new SimpleInjectorJobProcessorHostedService(container));

这样,您运行的实际作业就可以忽略ASP.NET Core,并且可以定义如下:
public class CoolJob : IMyJob
{
    private readonly IJobRepository repo;

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

    public void DoWork() => ...
}

而且所有作业都可以在Simple Injector中注册,如下所示:
 // NOTE: Simple Injector v4.3 API
container.Collection.Register<IMyJob>(typeof(CoolJob).Assembly);

关于c# - 使用ASP.NET Core 2.0将简单的Injector组件注入(inject)IHostedService,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50394666/

10-12 12:49