Use InstancePerLifetimeScope or InstancePerDependency when registering for controllers and hosted services?

Viewed 422

I'm upgrading my project from classic ASP.Net to Asp.Net Core 3.1. In the old project I used to register my UoW and services like this:

builder.Register(x => new UnitOfWork())
    .As(typeof(IUnitOfWork))
    .InstancePerLifetimeScope();

builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
    .Where(t => t.Name.EndsWith("Service"))
    .AsImplementedInterfaces()
    .InstancePerLifetimeScope();

Now, in the new project I'm using hosted services which I call within a controller action. So I created a BackgroundQueue and QueuedService implementation and registering like this:

builder.RegisterType<BackgroundQueue>()
    .As<IBackgroundQueue>()
    .SingleInstance();

builder.RegisterType<QueuedService>()
    .As<IHostedService>()
    .InstancePerDependency();

A controller action might use one particular service. In such cases a lifetime scope is enough because instances are disposed of when the request ends. Now, a particular controller action will spawn a new queued service, such as:

backgroundQueue.QueueBackgroundWorkItem(async ct => { //Do stuff... });  

The task might need to use an instance of the same particular service. In such a case, the task needs to create a completely new instance because the the task will keep on running long after the request has ended.

So, if I register the UoW and all my services always as InstancePerDependency, this should satisfy both cases?

1 Answers

If the background job uses other services, for example DbContext which is registered as InstancePerLifetimeScope, it may need to use a child scope with services registered for the scope of the job:

public class MyController
{
    public MyController(ILifetimeScope parentScope)
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<MyDbContext>()
            .WithParameters(new[] { new TypedParameter(typeof(DbContextOptions), MasterDbContext.GetOptions(masterDbConnectionString)) })
            .InstancePerLifetimeScope()
           .AsSelf();
        [..other services]

        var container = builder.Build();

        backgroundQueue.QueueBackgroundWorkItem(async ct => 
        { 
            using(container)
            {
                //Do stuff...
            } 
        }); 
    }
}
Related