title: Implementing Hangfire with Simple Injector in a net.core WebApi
body: <p><strong>NOTE</strong>:see the edits at the end.</p>
  <p>Trying my hand at understanding how to implement Hangfire + Simple Injector in a net core Web Api test project (already used SI in an asp.net project [but setting up core is quite the different beast] and HF in a net core project [using the default container]) so that i can then move on to use it to modify an existing project that uses the default Microsoft DI container.</p>
  <p>The project is using these packages</p>
  <blockquote>
  <p>Hangfire 1.6.17</p>
  <p>HangFire.SimpleInjector.Core 0.0.1</p>
  <p>SimpleInjector 4.0.12</p>
  <p>SimpleInjector.Integration.AspNetCore 4.0.12</p>
  <p>SimpleInjector.Integration.AspNetCore.Mvc 4.0.12</p>
  </blockquote>
  <p>My code at the moment</p>
  <h2>Startup</h2>
  <pre><code>using System;
  using Microsoft.AspNetCore.Builder;
  using Microsoft.AspNetCore.Hosting;
  using Microsoft.Extensions.Configuration;
  using Microsoft.Extensions.DependencyInjection;
  using Microsoft.Extensions.Logging;
  using Microsoft.AspNetCore.Routing;
  using Hangfire;
  using TestSimpleInjector.Services;
  using TestSimpleInjector.Controllers;
  using TestSimpleInjector.Model;
  using Newtonsoft.Json;
  using SimpleInjector;
  using Hangfire.SimpleInjector;
  using SimpleInjector.Integration.AspNetCore.Mvc;
  using Microsoft.AspNetCore.Http;
  using Microsoft.AspNetCore.Mvc.Controllers;
  using Microsoft.AspNetCore.Mvc.ViewComponents;
  using SimpleInjector.Lifestyles;
  using SimpleInjector.Diagnostics;
  
  namespace TestSimpleInjector
  {
  public Startup(IHostingEnvironment env)
      {
          var builder = new ConfigurationBuilder()
              .SetBasePath(env.ContentRootPath)
              .AddJsonFile(&quot;appsettings.json&quot;, optional: true, reloadOnChange: true)
              .AddEnvironmentVariables();
          Configuration = builder.Build();
      }
  
      private readonly Container ApplicationContainer = new Container();
  
      public IConfigurationRoot Configuration { get; private set; }
  
      // ConfigureServices is where you register dependencies. This gets
      // called by the runtime before the Configure method, below.
      public void ConfigureServices(IServiceCollection services)
      {
          // Add services to the collection.
          services.AddHangfire(config =&gt;
              config.UseSqlServerStorage(Configuration.GetConnectionString(&quot;HangfireConnection&quot;)));
          
          services.AddMvc().AddJsonOptions(opt =&gt;
          {
              opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
          });
  
          services.AddSingleton(Configuration);
          //services.AddTransient&lt;ITest, Test&gt;();
          //services.AddDbContext&lt;TestDbContext&gt;(ServiceLifetime.Transient);
          var SimpleInjector = new SimpleInjectorJobActivator(ApplicationContainer, Lifestyle.Scoped);
  
          GlobalConfiguration.Configuration.UseActivator(SimpleInjector);
  
          IntegrateSimpleInjector(services);
      }
  
      private void IntegrateSimpleInjector(IServiceCollection services)
      {
          ApplicationContainer.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
  
          services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
  
          services.AddSingleton&lt;IControllerActivator&gt;(
              new SimpleInjectorControllerActivator(ApplicationContainer));
          services.AddSingleton&lt;IViewComponentActivator&gt;(
              new SimpleInjectorViewComponentActivator(ApplicationContainer));
  
          services.EnableSimpleInjectorCrossWiring(ApplicationContainer);
          services.UseSimpleInjectorAspNetRequestScoping(ApplicationContainer);
      }
  
      private void InitializeContainer(IApplicationBuilder app)
      {
          // Add application presentation components:
          ApplicationContainer.RegisterMvcControllers(app);
          ApplicationContainer.RegisterMvcViewComponents(app);
  
          // Add application services. For instance:
          ApplicationContainer.Register&lt;ITest, Test&gt;(Lifestyle.Scoped);
          var registration = Lifestyle.Scoped.CreateRegistration(typeof(TestDbContext), ApplicationContainer);
          registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent,
              &quot;Mvc controller&quot;);
  
          //ApplicationContainer.AddRegistration(typeof(TestDbContext), registration);
          ApplicationContainer.AddRegistration&lt;TestDbContext&gt;(registration);
  
          // Cross-wire ASP.NET services (if any). For instance:
          ApplicationContainer.CrossWire&lt;ILoggerFactory&gt;(app);
  
          // NOTE: Do prevent cross-wired instances as much as possible.
          // See: https://simpleinjector.org/blog/2016/07/
      }
  
      // Configure is where you add middleware. This is called after
      // ConfigureServices. You can use IApplicationBuilder.ApplicationServices
      // here if you need to resolve things from the container.
      public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
      {
          loggerFactory.AddConsole(this.Configuration.GetSection(&quot;Logging&quot;));
          loggerFactory.AddDebug();
  
          InitializeContainer(app);
  
          ApplicationContainer.Verify();
  
          app.UseMvc(ConfigureRoutes);
  
          // If you want to dispose of resources that have been resolved in the
          // application container, register for the &quot;ApplicationStopped&quot; event.
          // You can only do this if you have a direct reference to the container,
          // so it won't work with the above ConfigureContainer mechanism.
          var options = new BackgroundJobServerOptions
          {
              Queues = new[] { &quot;coda-Sms&quot;, &quot;default&quot; }
          };
          app.UseHangfireDashboard();
          app.UseHangfireServer(options);
          app.UseMvc(ConfigureRoutes);
      }
  
      private void ConfigureRoutes(IRouteBuilder routeBuilder)
      {
          routeBuilder.MapRoute(&quot;default&quot;, &quot;{controller=Test}/{action=Index}/{id?}&quot;);
      }
  }
  </code></pre>
  <h2>Context</h2>
  <pre><code>using Microsoft.EntityFrameworkCore;
  using Microsoft.Extensions.Configuration;
  using System.IO;
  
  namespace TestSimpleInjector.Model
  {
      public class TestDbContext : DbContext
      {
          //public TestDbContext(DbContextOptions&lt;TestDbContext&gt; options) : base(options)
          //{
          //}
          public TestDbContext() : base()
          {
          }
  
          public DbSet&lt;VIR_NetSizeSmsLog&gt; VIR_NetSizeSmsLog { get; set; }
  
          public IConfigurationRoot Configuration { get; set; }
  
          protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
          {
              var builder = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile(&quot;appsettings.json&quot;, optional: true, reloadOnChange: true);
              builder.AddEnvironmentVariables();
              Configuration = builder.Build();
              var conStr = Configuration.GetConnectionString(&quot;Test2&quot;);
              optionsBuilder.UseSqlServer(conStr);
          }
      }
  }
  </code></pre>
  <h2>Repository</h2>
  <pre><code>using System.Collections.Generic;
  using TestSimpleInjector.Model;
  
  namespace TestSimpleInjector.Services
  {
      public interface ITest
      {
          IEnumerable&lt;VIR_NetSizeSmsLog&gt; GetAll();
      }
  }
  
  
  using System.Collections.Generic;
  using TestSimpleInjector.Model;
  
  namespace TestSimpleInjector.Services
  {
      public class Test : ITest
      {
          private readonly TestDbContext _context;
  
          public Test(TestDbContext context)
          {
              _context = context;
          }
  
          public IEnumerable&lt;VIR_NetSizeSmsLog&gt; GetAll()
          {
              return _context.VIR_NetSizeSmsLog;
          }
      }
  }
  </code></pre>
  <h2>Controller</h2>
  <pre><code>using Hangfire;
  using Microsoft.AspNetCore.Mvc;
  using System;
  using System.Collections.Generic;
  using TestSimpleInjector.Services;
  
  namespace TestSimpleInjector.Controllers
  {
      [Route(&quot;api/[controller]&quot;)]
      public class ValuesController : Controller
      {
          private readonly ITest _test;
  
          public ValuesController(ITest test)
          {
              _test = test;
          }
  
          // GET api/values
          [HttpGet]
          public IEnumerable&lt;string&gt; GetAll()
          {
              var values = _test.GetAll();
              List&lt;string&gt; listValues= new List&lt;string&gt;();
              foreach (var item in values)
              {
                  listValues.Add(item.idSms.ToString());
              }
              var jobId = BackgroundJob.Schedule&lt;ITest&gt;(services =&gt; services.GetAll(), TimeSpan.FromMinutes(1));
              return listValues;
          }
  }
  </code></pre>
  <p>When i try to test it,the solution compiles and runs,the job gets scheduled and fired by hangfire (so the Simple injector job activator seems to be working) but unless i add these services in Startup by removing the comment</p>
  <pre><code>//services.AddTransient&lt;ITest, Test&gt;();
  //services.AddDbContext&lt;TestDbContext&gt;
  </code></pre>
  <p>When hangfire tries to execute the scheduled process i get this error</p>
  <pre><code>System.InvalidOperationException
  
  A suitable constructor for type 'TestSimpleInjector.Services.ITest' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
  
  System.InvalidOperationException: A suitable constructor for type 'TestSimpleInjector.Services.ITest' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
     at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
     at Hangfire.AspNetCore.AspNetCoreJobActivatorScope.Resolve(Type type)
     at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
     at Hangfire.Server.BackgroundJobPerformer.&lt;&gt;c__DisplayClass8_0.&lt;PerformJobWithFilters&gt;b__0()
     at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func`1 continuation)
     at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable`1 filters)
     at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)
     at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)
  </code></pre>
  <p>From what i'm understanding because the constructor for Test takes concrete instance of the dbcontext as parameter,so HF cant actually understand how to build it.</p>
  <p>As soon as i add those 2 service registrations,everything works fine.But if i register them as services that way,isnt it the same as using the default DI on those components,losing the point of implementing SI?Also,this is considering that i'm not missing anything else / implementing something else wrong</p>
  <p>I could really use some pointers on what's the correct way to do it</p>
  <h2>EDIT</h2>
  <p>After @Steven comment below i changed the startup <strong>ConfigurationService</strong> code to</p>
  <pre><code>        public void ConfigureServices(IServiceCollection services)
          {
              var SimpleInjector = new SimpleInjectorJobActivator(ApplicationContainer, Lifestyle.Scoped);
              // Add services to the collection.
              services.AddHangfire(config =&gt;
                  config.UseSqlServerStorage(Configuration.GetConnectionString(&quot;HangfireConnection&quot;)).UseActivator(SimpleInjector));
             
              services.AddMvc().AddJsonOptions(opt =&gt;
              {
                  opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
              });
  
              services.AddSingleton(Configuration);
              //services.AddTransient&lt;ITest, Test&gt;();
              //services.AddDbContext&lt;TestDbContext&gt;(ServiceLifetime.Transient);
  
              //var SimpleInjector = new SimpleInjectorJobActivator(ApplicationContainer, Lifestyle.Scoped);
              //GlobalConfiguration.Configuration.UseActivator(SimpleInjector);
  
              IntegrateSimpleInjector(services);
          }
  </code></pre>
  <p>Now the scheduled job fires but i get</p>
  <pre><code>SimpleInjector.ActivationException
  
  The Test is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.
  
  SimpleInjector.ActivationException: The Test is registered as 'Async Scoped' lifestyle, but the instance is requested outside the context of an active (Async Scoped) scope.
     at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
     at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
     at lambda_method(Closure )
     at SimpleInjector.InstanceProducer.GetInstance()
     at SimpleInjector.Container.GetInstance(Type serviceType)
     at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
     at Hangfire.Server.BackgroundJobPerformer.&lt;&gt;c__DisplayClass8_0.&lt;PerformJobWithFilters&gt;b__0()
     at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func`1 continuation)
     at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable`1 filters)
     at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)
     at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)
  </code></pre>
  <p>Unless i change the LifeStyles of Test and TestDbContext to <strong>Transient</strong> (that done,the scheduled job runs fine).
  This however is simpler to understand (i think):the asp.net version of SimpleInjector can actually start scopes by itself when they are required for the Async Scope.Not so on the net core version.
  Now just need to understand how (and where) to create the scopes....</p>
  <h2>Edit 2</h2>
  <p>Think i might have solved the step in the edit above leveraging Hangfire filters to create a scope when the job gets performed</p>
  <p>So,added a new class</p>
  <pre><code>public class SimpleInjectorAsyncScopeFilterAttribute : JobFilterAttribute, IServerFilter
      {
          private static readonly AsyncScopedLifestyle lifestyle = new AsyncScopedLifestyle();
  
          private readonly Container _container;
  
          public SimpleInjectorAsyncScopeFilterAttribute(Container container)
          {
              _container = container;
          }
  
          public void OnPerforming(PerformingContext filterContext)
          {
              AsyncScopedLifestyle.BeginScope(_container);
          }
  
          public void OnPerformed(PerformedContext filterContext)
          {
              var scope = lifestyle.GetCurrentScope(_container);
              if (scope != null)
                  scope.Dispose();
          }
      }
  </code></pre>
  <p>To pass an AsyncScope to the job on starting and disposing of it at job's end</p>
  <p>Then,back in Startup.ConfigurationService i changed the Hangfire registration once again to</p>
  <pre><code>services.AddHangfire(config =&gt;
                  config.UseSqlServerStorage(Configuration.GetConnectionString(&quot;HangfireConnection&quot;)).UseActivator(SimpleInjector).UseFilter(new SimpleInjectorAsyncScopeFilterAttribute(ApplicationContainer)));
  </code></pre>
  <p>to activate the filter.</p>
  <p>Now the scheduled job runs fine on the AsyncScope that gets served to it.</p>
  <p>What's left is to understand if this implementation is actually ok for production use or has problems down the line that i'm not seeing yet</p>