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

后端 未结 2 1091
耶瑟儿~
耶瑟儿~ 2020-12-31 09:48

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/funda

2条回答
  •  时光取名叫无心
    2020-12-31 09:57

    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();
    
    // Cross-wire TimedService in the built-in configuration system
    services.AddSingleton(
        c => container.GetInstance());
    

    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.

    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.

    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.

    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())
                {
                    service.DoWork();
                }
            }
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            this.timer?.Change(Timeout.Infinite, 0);
            return Task.CompletedTask;
        }
    
        public void Dispose() => this.timer?.Dispose();
    }
    

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

    services.AddSingleton(
        new SimpleInjectorJobProcessorHostedService(container)); 
    

    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() => ...
    }
    

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

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

提交回复
热议问题