What is the scope of a DI scope - Hangfire in .NET Core
Asked Answered
M

2

12

Imagine in ASP.NET Core I register a dependency as Scoped like the following:

IServiceCollection services = ...
services.AddScoped<IEmailService, EmailService>();

Then I know that for each HTTP request a new scope will be created and a new instance of Email service will be reused. Moreover, the same scope will persist for the lifetime of a request.

Now, imagine I add a Hangfire Background Job like this:

RecurringJob.AddOrUpdate<IServiceA>("DoA", s => s.DoA(), Cron.Daily());

where

public class ServiceA: IServiceA {
    public ServiceA(IEmailService emailService) { ... }
    public void DoA() { ... }
}

I would like to understand what does scoped mean in hangfire job terms, by default, does hangfire

  • use a single scope for all the jobs and runs
  • create separate scopes for each job, but different runs of the same job share the scope
  • create separate scopes for each run of any job

Bonus points for an explanation on how to configure it.

Molotov answered 26/8, 2020 at 11:23 Comment(3)
The query boils down to does Hangfire generate scopes or do I have to do it explicitly? Given that a Hangfire service can run indefinitely #1 and #2 would be meaningless, effectively behaving like singletonsPeople
Looks like this is provided out of the box if you simply call AddHangFire in your DI configuration. Each execution is wrapped in a scope, no matter what DI container is used. This isn't documented anywhere thoughPeople
Thanks, I would be happy to accept this as an answerMolotov
R
3

As Panagiotis Kanavos put in a comment above (but to make more visible as an accepted answer):

Looks like this is provided out of the box if you simply call AddHangFire in your DI configuration. Each execution is wrapped in a scope, no matter what DI container is used. This isn't documented anywhere though

Radically answered 4/1, 2022 at 18:57 Comment(4)
I am seeing som odd caching behavior that could indicate otherwise, need to verify the implementation i thinkLucius
Can confirm that it is not using microsoft scopes. That create scope that is linked to is from hangfire JobActivator - not MS DILucius
i was wrong, there is a dotnet core scope handing- however i do experience like the dbcontext is cached. FindAsync returns a value from context and not latest from db , like its being cached over multiple job runsLucius
@PoulK.Sørensen did you ever find a resolution for this? I'm seeing some interesting behavior with scoped resources and hangfire, so I'd love to know if it became too much of a headache that you just had to use something else for DI (e.g. Windsor)Orientation
H
2

Please have a look at the implementation here: https://github.com/HangfireIO/Hangfire/blob/7eb4b3fd56abe8eeed692265aff39e2d694e86df/src/Hangfire.Core/Server/CoreBackgroundJobPerformer.cs#L47

using (var scope = _activator.BeginScope(context))
        {
            object instance = null;

            if (context.BackgroundJob.Job == null)
            {
                throw new InvalidOperationException("Can't perform a background job with a null job.");
            }
            
            if (!context.BackgroundJob.Job.Method.IsStatic)
            {
                instance = scope.Resolve(context.BackgroundJob.Job.Type);

                if (instance == null)
                {
                    throw new InvalidOperationException(
                        $"JobActivator returned NULL instance of the '{context.BackgroundJob.Job.Type}' type.");
                }
            }

            var arguments = SubstituteArguments(context);
            var result = InvokeMethod(context, instance, arguments);

            return result;
        }

Hangfire creates a scope before resolving your class, this means you can use and configure scoped instances in your dependency container as you do in ASP.net

Hoitytoity answered 29/9, 2023 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.