I just had this idea to derive the lifetime scopes of instances automatically from their dependencies.
Given that
… the “intended” lifetime scope of all resolved instances can be derived by one simple rule:
Of course there is one exception: Func<> returned instances and their arguments “need” to be created in a new nested scope.
An example (simplified to constructors):
Whenever a Func<PersonName, Person> generator is used, PersonName is bound to the new lifetime scope and implicitly forces PersonsFriends also to be bound to the same scope, because the dependency PersonName can be fulfilled.
This solves one essential problem with dependency injectors with nested lifetime scoping:
If an instance of PersonsFriends already exists in an ancestor scope, it would be used instead of a new instance. In other words, it would just silently “shadow” an instance that is parameterized differently.
Now, by tracking down the closure of all dependencies, the lifetime scope chosen for the instance would be just right.
This problem was bothering me for some time and was indeed the most confusing behavior of my constructor dependency injector.