简体   繁体   中英

How can a dependency (registered in nested LifetimeScope) be resolved from autofac container

Is there any way to resolve the dependency (which is actually registered in nested LifetimeScope, not in container) from container?

Explanation of Actual Implementation:

I have ApplicationContext class (with IServiceProvider injected in its constructor) containing current-user's basic info. It first resolves IHttpContextAccessor from serviceProvider , then extract user's info from httpContextAccessor.HttpContext . ApplicationContext class is injected into all repositories/services.

But, In some static classes, I am resolving ApplicationContext class from static IoC class (autofac container wrapped in it). And I think this is the only solution, because I can't inject into static constructor.

I am implementing event-bus for which I have created EventBusContext class which receives user-info from Event Data.

ApplicationContext class try to resolve EventBusContext from IServiceProvider and extract user-info from it ONLY if it gets HttpContext as null ( which means this execution is not started from Http Request ).

Once EventBus class receives an event from RabbitMQ, it creates EventBusConext class, add users info into it and registers it on the fly into newly created nested LifetimeScope then resolves the EventHandler class and invoke Handle method (via reflection).

Everything works perfect! The issue occurs ONLY when EventHandler uses any class which resolves ApplicationContext class from static IoC class, because then static IoC class internally tries to resolves EventBusContext from autofac container (which is wrapped in it) but it fails to do so.

I'm going to focus on the initial question to start:

Is there any way to resolve the dependency (which is actually registered in nested LifetimeScope, not in container) from container?

The short answer is no. If something is registered/added in a child scope, there's no way a parent scope will know about it.

Autofac scopes are hierarchical. There is a lot of documentation on this including a diagram to illustrate that.

Trying to boil it down to "rules," we could say:

  • Registration "rules":
    • You can register things into the root container.
    • When you create a child lifetime scope, you can add new registrations that exist just for that scope.
  • Resolution "rules":
    • When you resolve a service it will only know about things that are registered in the lifetime scope from which you're resolving and any parents.
    • If you resolve a singleton it will always resolve from the root lifetime scope, including all its dependencies.
    • Child scopes know about parent scopes.
    • Parent scopes do not hold references or "know" about child scopes.

This is very simplified but is close enough to illustrate the point.

If you have things added dynamically to a child lifetime scope you'll only have access to them in that child lifetime scope and any child scopes you create from there.

var builder = new ContainerBuilder();
builder.RegisterType<A>();
var container = builder.Build();

// This will work (though it's recommended you avoid resolving things
// from containers because it can lead to memory leaks).
// https://autofac.readthedocs.io/en/latest/lifetime/index.html
container.Resolve<A>();

// This will NOT work. The container doesn't have B in it.
container.Resolve<B>();

using(var scope1 = container.BeginLifetimeScope(b => b.RegisterType<B>())
{
  // This will work. The child scope knows about parent registrations.
  scope1.Resolve<A>();

  // This will also work. The registration was added to the scope.
  scope1.Resolve<B>();

  // This will STILL NOT WORK. The parent does NOT know about child scopes.
  container.Resolve<B>();

  using(var scope2 = scope1.BeginLifetimeScope())
  {
    // These will work. The child scope knows about parent registrations.
    scope2.Resolve<A>();
    scope2.Resolve<B>();

    // This will STILL NOT WORK.
    container.Resolve<B>();
  }
}

There is no hackery or workaround for this. It's intentionally hierarchical.

If you have something that needs access to request-level items (or otherwise needs things that are registered in a child lifetime scope dynamically) the only way to get them is to resolve from that child lifetime scope or a nested child.

Getting more specific to the goal:

From the implementation description, you have some things that intentionally run outside a request and resolve from the root container (again, not a great idea, but it is what it is). If those things suddenly need items that are registered on the fly by a request, then you have basically two options, both of which are "you need to change your architecture/design."

  1. Switch all the items that run "outside a request" to run "inside a request" so they have access to the dynamically registered things.
  2. Change method signatures or whatever so the dynamically registered things aren't dependencies of the "outside-the-request" items - make those dynamic things get passed in as method parameters.

Actually providing a fully updated design or "working code" illustrating either of those things is more a consulting effort than an answer to the question. Unfortunately, though, the solution to getting to that per request data means you need to change your design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM