Search code examples
c#autofac

Hierarchical scope after Dependency injection v. 8.0.0


Ive been trying to upgrade to latest Autofac/Multitenant/Dependency injection.

Release 8.0.0 states that there is a breaking change: https://github.com/autofac/Autofac.Extensions.DependencyInjection/releases/tag/v8.0.0

IServiceScopeFactory is now a singleton and child scopes are flat, not hierarchical.

Right now my Controllers fail when they try to resolve a dependency which is inside the "Parent lifetime scope", as the parent lifetime scope is just "root" now.

Previously the "Parent lifetime scope" was a "tenant lifetime" (as im using multitenant package) and then it worked.

I just cannot figure out how/where to apply the below "fix" in ASP.NET Core and have hierachical scopes as in previous versions.

I have copied the samples from here: https://github.com/autofac/Autofac.AspNetCore.Multitenant/tree/develop/samples/Sandbox.AspNetCore5_To_6

and it works as expected (scopes are flat and resolved from root (not tenant-scope))

Where do I put this code to control the created scopes etc. ?

// Based on an IServiceProvider...
IServiceProvider provider = CreateServiceProvider();

// You'll need to get an Autofac lifetime scope.
var autofacScope = provider.GetService<ILifetimeScope>();

// Use the Autofac constructs to create hierarchical lifetimes.
var unitOfWorkOutside = autofacScope.BeginLifetimeScope();

// And later have a sub-unit-of-work scope inside that...
var unitOfWorkInside = unitOfWorkOutside.BeginLifetimeScope();

// Now they're related so they'll share a hierarchy. If you dispose the outer scope...
unitOfWorkOutside.Dispose();

// ...stuff in the inner scope will not resolve because you disposed its parent.
unitOfWorkInside.Resolve<MyService>();

Solution

  • If you need a hierarchical lifetime scope now, you need to get the current request scope and manually create it. This means either resolving ILifetimeScope from an IServiceProvider or injecting ILifetimeScope into your controller and resolve things using service location.

    Here's getting it from the service provider:

    public class MyController
    {
      public IActionResult DoWork()
      {
        var requestScope = this.Context.RequestServices.GetService<ILifetimeScope>();
        using var unitOfWorkScope = this._requestScope.BeginLifetimeScope();
        var thing = unitOfWorkScope.Resolve<Thing>();
        return thing.DoWork();
      }
    }
    

    Here's injecting ILifetimeScope into the controller:

    public class MyController
    {
      private ILifetimeScope _requestScope;
    
      public MyController(ILifetimeScope requestScope)
      {
        this._requestScope = requestScope;
      }
    
      public IActionResult DoWork()
      {
        using var unitOfWorkScope = this._requestScope.BeginLifetimeScope();
        var thing = unitOfWorkScope.Resolve<Thing>();
        return thing.DoWork();
      }
    }
    

    In both cases, the request lifetime scope should be a child of the tenant scope. The only way to make a child of that child is to use Autofac directly.