Rectangle 27 0

asp.net mvc Autofac: any way to resolve the innermost scope?


// Consuming class like this...
public class BigComponent
{
  public void DoSomethingCool()
  {
    using(var scope = ...)
    {
      var c = scope.Resolve<SubComponent>();
      c.DoWork();
    }
  }
}

// ...and container registrations like this...
builder.RegisterType<BigComponent>().SingleInstance();
// Consuming class like this...
public class BigComponent
{
  private SubComponent _c;
  public BigComponent(SubComponent c)
  {
    _c = c;
  }
  public void DoSomethingCool()
  {
    _c.DoWork();
  }
}

// ...and container registrations like this...
builder.RegisterType<BigComponent>().InstancePerDependency();
builder.RegisterType<SubComponent>().InstancePerLifetimeScope();
public class TestLifetimeScopeProvider : ILifetimeScopeProvider
{
    readonly ILifetimeScope _container;
    private ILifetimeScope _lifetimeScope = null;

    public TestLifetimeScopeProvider(ILifetimeScope container)
    {
        if (container == null) throw new ArgumentNullException("container");
        _container = container;
    }

    public ILifetimeScope ApplicationContainer
    {
        get { return _container; }
    }

    public ILifetimeScope GetLifetimeScope()
    {
        if (_lifetimeScope == null)
        {
            _lifetimeScope = ApplicationContainer.BeginLifetimeScope("httpRequest")
        }
        return _lifetimeScope;
    }

    public void EndLifetimeScope()
    {
        if (_lifetimeScope != null)
            _lifetimeScope.Dispose();
    }
}
var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);
var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope;
using (var scope = requestScope.BeginLifetimeScope(cb => {
  cb.RegisterType<X>().InstancePerLifetimeScope();
  // ...
}))
{
    var comp = scope.Resolve<X>();
    // ...
}

Again, just a stub for unit testing, not something you'd ever use in production.

BTW: with innermost scope I meant the most deeply nested scope. For me, the application container is the outermost scope.

Generally speaking, I try to invert this stuff. If I have some application-level thing with cached objects, I'll take those objects as parameters into my plugins rather than vice-versa. Intentional design to avoid the service location, if you will. Sometimes it's unavoidable, but I can't help but think there's a "code smell" here you could address with a little refactoring.

If you're stuck doing service location, you'll need to use AutofacDependencyResolver.Current.ApplicationContainer if you need the absolute innermost scope, but keep in mind any objects you register scoped to InstancePerHttpRequest will not be resolvable if you do that, so you could get into trouble. It really is recommended to use the AutofacDependencyResolver.Current.RequestLifetimeScope instead. That would make your method:

In Autofac, the innermost scope is always the container. Using the AutofacDependencyResolver, it'd be AutofacDependencyResolver.Current.ApplicationContainer

In a testing environment, the AutofacDependencyResolver lets you swap in the provider that dictates how request lifetimes get generated. You can implement a simple/stub one like this:

It sounds like your SingleInstance component is doing some sort of service location, basically, with manual registration/resolution of certain components. If the set of types being registered is fixed, I might recommend (if possible) some redesign of your system, so the SingleInstance component isn't registered as SingleInstance anymore and instead gets registered as InstancePerDependency, then have that take these other items in as constructor parameters.

My misunderstanding on reference to innermost/outermost, sorry. Either way, you can't really walk the stack in either direction, so if you have to do the service location, you're stuck with AutofacDependencyResolver. Note if you use MVC IDependencyResolver, then DependencyResolver.Current.GetService will always come out of the request lifetime, so you may not need that lambda... oh, except you're registering on the fly. So, yep, you'll still need that.

Problably my example wasn't that great. What I try to achieve all happens during initialization to set up the container. I don't know the types of the components to register until runtime, so I can't resolve them statically. Instead, I register them dynamically and resolve them directly so that their dependencies are nicely resolved without explicit service location. I'll be back on sunday with a better description of what I'm trying to do. Thanks so far already for your time. Probably it'll boil down to my Func<ILifetimeScope> approach, as I can't seem to get the innermost scope otherwise.

The "BigComponent" in my case is a registry that holds certain objects for the whole life of the application. The nested scope is used during application startup (first HTTP request) to instantiate some types discovered dynamically (plugin architecture). My current solution is to inject a Func<ILifetimeScope> into the "BigComponent" that is currently registered as ()=>AutofacDependencyResolver.Current.RequestLifetimeScope. Still not great, but it leaves the static reference to the service locator out of the scope. [continued]

The idea is to not have to do the on-the-fly registration-and-immediate-resolution thing.

Then when you wire up the DependencyResolver in your test, you provide your lifetime scope provider:

There is no way from a nested scope (if all you have is an ILifetimeScope) to "walk backward" to get to the container. I'm not necessarily sure you want to do that, anyway.

This lets you use InstancePerHttpRequest and such inside unit tests without actually having a real request context. It also means you should be able to use the request lifetime scope in your registration/resolution method and not have to fall back on the application container.

You might try inverting it a bit:

Note