diff --git a/src/Autofac/Core/ISharingLifetimeScope.cs b/src/Autofac/Core/ISharingLifetimeScope.cs index 004bebe81..2395cc34c 100644 --- a/src/Autofac/Core/ISharingLifetimeScope.cs +++ b/src/Autofac/Core/ISharingLifetimeScope.cs @@ -55,8 +55,11 @@ public interface ISharingLifetimeScope : ILifetimeScope /// possible secondary qualifying GUID key. /// /// Key to look up. - /// Secondary key to look up. - /// The instance that has the specified key. + /// + /// Secondary key to look up, to better identify an instance that wraps around another instance + /// or is otherwise "namespaced" by it. + /// + /// The instance that has the specified keys. /// true if the key was found; otherwise, false. bool TryGetSharedInstance(Guid primaryId, Guid? qualifyingId, out object value); @@ -73,7 +76,10 @@ public interface ISharingLifetimeScope : ILifetimeScope /// possible secondary qualifying GUID key. /// /// Key. - /// Secondary key. + /// + /// Secondary key, to better identify an instance that wraps around another instance + /// or is otherwise "namespaced" by it. + /// /// A function that will create the instance when called. /// The shared instance. object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func creator); diff --git a/src/Autofac/Core/Lifetime/LifetimeScope.cs b/src/Autofac/Core/Lifetime/LifetimeScope.cs index 01429fb00..c7d02abc4 100644 --- a/src/Autofac/Core/Lifetime/LifetimeScope.cs +++ b/src/Autofac/Core/Lifetime/LifetimeScope.cs @@ -48,7 +48,8 @@ public class LifetimeScope : Disposable, ISharingLifetimeScope, IServiceProvider /// Protects shared instances from concurrent access. Other members and the base class are threadsafe. /// private readonly object _synchRoot = new object(); - private readonly ConcurrentDictionary<(Guid, Guid?), object> _sharedInstances = new ConcurrentDictionary<(Guid, Guid?), object>(); + private readonly ConcurrentDictionary _sharedInstances = new ConcurrentDictionary(); + private readonly ConcurrentDictionary<(Guid, Guid), object> _sharedQualifiedInstances = new ConcurrentDictionary<(Guid, Guid), object>(); private object? _anonymousTag; private LifetimeScope? parentScope; @@ -82,7 +83,7 @@ protected LifetimeScope(IComponentRegistry componentRegistry, LifetimeScope pare /// Components used in the scope. public LifetimeScope(IComponentRegistry componentRegistry, object tag) { - _sharedInstances[(SelfRegistrationId, null)] = this; + _sharedInstances[SelfRegistrationId] = this; ComponentRegistry = componentRegistry ?? throw new ArgumentNullException(nameof(componentRegistry)); Tag = tag ?? throw new ArgumentNullException(nameof(tag)); RootLifetimeScope = this; @@ -292,35 +293,56 @@ public object ResolveComponent(ResolveRequest request) /// public object CreateSharedInstance(Guid id, Func creator) { - return CreateSharedInstance(id, null, creator); + if (creator == null) throw new ArgumentNullException(nameof(creator)); + lock (_synchRoot) + { + if (_sharedInstances.TryGetValue(id, out var result)) return result; + + result = creator(); + if (_sharedInstances.ContainsKey(id)) + throw new DependencyResolutionException(string.Format(CultureInfo.CurrentCulture, LifetimeScopeResources.SelfConstructingDependencyDetected, result.GetType().FullName)); + + _sharedInstances.TryAdd(id, result); + + return result; + } } /// public object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func creator) { if (creator == null) throw new ArgumentNullException(nameof(creator)); + if (qualifyingId == null) + { + return CreateSharedInstance(primaryId, creator); + } lock (_synchRoot) { - var instanceKey = (primaryId, qualifyingId); + var instanceKey = (primaryId, qualifyingId.Value); - if (_sharedInstances.TryGetValue(instanceKey, out var result)) return result; + if (_sharedQualifiedInstances.TryGetValue(instanceKey, out var result)) return result; result = creator(); - if (_sharedInstances.ContainsKey(instanceKey)) + if (_sharedQualifiedInstances.ContainsKey(instanceKey)) throw new DependencyResolutionException(string.Format(CultureInfo.CurrentCulture, LifetimeScopeResources.SelfConstructingDependencyDetected, result.GetType().FullName)); - _sharedInstances.TryAdd(instanceKey, result); + _sharedQualifiedInstances.TryAdd(instanceKey, result); return result; } } /// - public bool TryGetSharedInstance(Guid id, out object value) => TryGetSharedInstance(id, null, out value); + public bool TryGetSharedInstance(Guid id, out object value) => _sharedInstances.TryGetValue(id, out value); /// - public bool TryGetSharedInstance(Guid primaryId, Guid? qualifyingId, out object value) => _sharedInstances.TryGetValue((primaryId, qualifyingId), out value); + public bool TryGetSharedInstance(Guid primaryId, Guid? qualifyingId, out object value) + { + return qualifyingId == null + ? TryGetSharedInstance(primaryId, out value) + : _sharedQualifiedInstances.TryGetValue((primaryId, qualifyingId.Value), out value); + } /// /// Gets the disposer associated with this container. Instances can be associated