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:
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."
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.