Reputation:
As shown in the runnable code example below I want to create a named scope wherein a certain instance of an object is resolved regardless of other unnamed scopes that are created after the object is created.
With regard to the documentation found here:
// You can't resolve a per-matching-lifetime-scope component
// if there's no matching scope.
using(var noTagScope = container.BeginLifetimeScope())
{
// This throws an exception because this scope doesn't
// have the expected tag and neither does any parent scope!
var fail = noTagScope.Resolve<Worker>();
}
In my case in the example below a parent scope DOES have a matching tag but it still does not work. Should it?
In the following example scopes are tidy and parent scopes are known - In my application only the root container object is accessible so when a scope is created it is always from the container not the parent scope.
public class User
{
public string Name { get; set; }
}
public class SomeService
{
public SomeService(User user)
{
Console.WriteLine($"Injected user is named {user.Name}");
}
}
class Program
{
private static IContainer container;
private const string USER_IDENTITY_SCOPE = "SOME_NAME";
static void Main(string[] args)
{
BuildContainer();
Run();
Console.ReadKey();
}
private static void BuildContainer()
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<SomeService>();
builder.RegisterType<User>().InstancePerMatchingLifetimeScope(USER_IDENTITY_SCOPE);
container = builder.Build();
}
private static void Run()
{
using (var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
{
User outerUser = outerScope.Resolve<User>();
outerUser.Name = "Alice"; // User Alice lives in this USER_IDENTITY_SCOPE
SomeService someService = outerScope.Resolve<SomeService>(); // Alice
// Now we want to run a "process" under the identity of a different user
// Inside of the following using block, we want all services that
// receive a User object to receive Bob:
using (var innerSope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
{
User innerUser = innerSope.Resolve<User>();
innerUser.Name = "Bob"; // We get a new instance of user as expected. User Bob lives in this USER_IDENTITY_SCOPE
// Scopes happen in my app that are unrelated to user identity - how do I retain User object despite this?
// The following is not a USER_IDENTITY_SCOPE -- We still want Bob to be the User object that is resolved:
using (var unnamedScope = container.BeginLifetimeScope())
{
// Crashes. Desired result: User Bob is injected
SomeService anotherSomeService = unnamedScope.Resolve<SomeService>();
}
}
}
}
}
Using Autofac 4.9.2 / .net core 2.2
Upvotes: 1
Views: 103
Reputation: 23924
In your example, you're launching the unnamed scope from the container, not from a parent with a name:
using (var unnamedScope = container.BeginLifetimeScope())
Switch that to be a child of a scope with a name and it'll work.
using (var unnamedScope = innerScope.BeginLifetimeScope())
I'd also note that you've named these outerScope
and innerScope
but innerScope
is not actually a child of the outerScope
so the names are misleading. Technically the two scopes are peers.
All three are direct children of the container. If you think about sharing the user in terms of scope hierarchy, you'd need to make child scopes from parents that have children.
You'll notice inner and outer are still peers - you can't have a parent and a child with the same name, so given inner and outer are both named, they'll never share the same hierarchy except for the container.
I would strongly recommend not trying to bypass a hierarchical model here. For example, say you really are trying to do this:
Which might look like this:
using(var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
using(var unnamedScope = container.BeginLifetimeScope())
{
//...
}
This is pretty much what you have in the snippet above. The only common sharing these scopes have is at the container level. If you tried to resolve something from the named scope and pass it to a peer scope, you run the risk of things being disposed out from under you or other weird hard-to-troubleshoot problems. Like if outerScope
gets disposed but unnamedScope
lives on, you can get into trouble.
// PLEASE DO NOT DO THIS. YOU WILL RUN INTO TROUBLE.
using(var outerScope = container.BeginLifetimeScope(USER_IDENTITY_SCOPE))
{
var user = outerScope.Resolve<User>();
using(var unnamedScope = container.BeginLifetimeScope(b => b.RegisterInstance(user)))
{
//...
}
}
That's bad news waiting to happen, from odd disposal problems to things not sharing the same set of dependencies when you think they should. But, you know, we can give you the gun, it's up to you to not shoot yourself in the foot with it.
Upvotes: 1