繁体   English   中英

使用ASP.NET Core 2.0将简单的Injector组件注入IHostedService

[英]Injecting Simple Injector components into IHostedService with ASP.NET Core 2.0

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

// 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注入组件寄存器(例如存储库,带有装饰器的通用处理程序......)到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中注册的,因此在运行定时后台服务时收到以下错误:

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

所以我的问题是,在Simple Injector中实现后台任务的最佳方法是什么? 这是否需要一个独立的集成包而不是标准的MVC集成? 我怎样才能将我的Simple Injector注册注入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>());

请注意,托管服务只解析一次,并且永久缓存,有效地使它们成为单身人士。 这就是为什么你应该把它作为Singleton注册在Simple Injector中。

但是,这样做的一个原因是,您将无法将任何ScopedTransient依赖项注入托管服务。 最重要的是,它会强制您让应用程序组件( 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);

我会挂钩HostBuilder的ConfigureContainer方法并设置simpleinjectore,如下所示:

                   IHostBuilder()
                   .ConfigureContainer<ServiceCollection>((builder, services) =>
                   {
                       var container = new Container();

                       container.RegisterSingleton<IJobRepository, JobRepository>();
                       services.AddTransient<IHostedService, TimedService>();

                   })
                   .ConfigureServices((hostContext, services) =>
                   {
                       // Originally we would have done this
                       //services.AddHostedService<Service>();
                   })
                   .Build();

        using (host)
        {
            await host.StartAsync();
            await host.WaitForShutdownAsync();
        }

虽然你可以使用你的IHostedService实现,但我认为它可能会隐藏正在发生的事情。 我认为基础设施引导应该在一个地方完成,或者至少在一个地方进行协调。 我认为容器是基础设施,并将通过HostBuilder方法将其全部设置为应用程序的其余部分。

一个额外的优点也可能是您没有完全替换ServiceCollection,因为它与其他框架相关的东西很好地工作。 我仍然会使用ServiceCollection做一些事情的例子:

                   IHostBuilder()
                   .ConfigureServices((hostContext, services) =>
                   {
                       services.AddLogging();
                       services.AddOptions();
                   })

这与simpleinjector docs中关于使用ASP.NET Core设置容器的内容一致:

使用Simple Injector的做法是使用Simple Injector来构建应用程序组件的对象图,并让内置容器构建框架和第三方组件。使用Simple Injector的实践是使用Simple Injector来构建对象图您的应用程序组件并让内置容器构建框架和第三方组件

同样适用于.net核心和通用HostBuilder。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM