Hangfire
Hangfire copied to clipboard
Asp.Net Core Singleton being activated multiple times
I have a singleton registered in my startup.cs like so services.AddSingleton<ITriggerWarmup, TriggerWarmup>();. I also am adding hangfire and the service like so services.AddHangfire(...) and services.AddHangfireServer(...). I know from looking at HangfireServiceCollectionExtensions that it should add the AspNetCoreJobActivator without me doing any extra work. That said, if I put a break point in the constructor of TriggerWarmup it gets hit once on app startup and then again when a job that targets it gets registered. Note, if i run that job multiple times after that initial run, the constructor doesn't get called again... so it seems like its kinda getting registered as a singleton but not really. Any thoughts?
If it helps TriggerWarmup looks something like this:
public class TriggerWarmup : ITriggerWarmup
{
public const string QueueName = "background-warmup-job-queue";
public TriggerWarmup(IBackgroundJobClient backgroundJobs)
{
BackgroundJobs = backgroundJobs;
EnqueuedState = new EnqueuedState { Queue = QueueName };
WarmupTask = new Lazy<int>(() =>
{
BackgroundJobs.Create(() => WarmupServices(), EnqueuedState);
return 1;
});
}
private IBackgroundJobClient BackgroundJobs { get; }
private IState EnqueuedState { get; }
private Lazy<int> WarmupTask { get; set; }
public int Start()
{
// Trigger task to run
var result = WarmupTask.Value;
return ...;
}
public async Task WarmupServices()
{
...
}
}
constructor of TriggerWarmup it gets hit once on app startup
How does the code that instantiates it during the startup look like, and is there any chance new TriggerWarmup is simply called there? This can also happen if another IoC container is registered somewhere by calling IGlobalConfiguration.UseXXXActivator.
I've checked the code of AspNetCoreJobActivator and it seems to be a bug there. It overrides the BeginScope method, but does not override ActivateJob method, as a result it simply calls Activator.CreateInstance, which produces new instance each time it is called. It seems that this part is in transition of rewrite, as multiple places are marked as obsolete with promise to be removed in 2.0 (I don't see branch related to that yet?).
Unit test to reproduce the issue:
class Test {
}
[Test]
public void Singleton()
{
var serviceProvider = new ServiceCollection()
.AddSingleton<Test>()
.BuildServiceProvider();
var activator = new AspNetCoreJobActivator(serviceProvider.GetService<IServiceScopeFactory>());
var job1 = activator.ActivateJob(typeof(Test));
var job2 = activator.ActivateJob(typeof(Test));
Assert.IsTrue(Object.ReferenceEquals(job1, job2));
}
Also I think AspNetCoreJobActivatorScope implementation is questionable.
It accepts IServiceScope as constructor parameter and then disposes it. I think for IDisposable rule of thumb is that creator is responsible for disposing instance it had created, it this case creator is AspNetCoreJobActivator and dispose is called by AspNetCoreJobActivatorScope (I guess reason for that is that AspNetCoreJobActivator does not know when to dispose).
How to solve this problem?