Skip to content

Commit

Permalink
Merge pull request #1117 from VonOgre/develop
Browse files Browse the repository at this point in the history
Fixes #1113- Adding multi-id qualification to stored instances in lifetime scope
  • Loading branch information
alistairjevans committed May 11, 2020
2 parents 3b4a080 + 48f857d commit 67717d5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 8 deletions.
26 changes: 26 additions & 0 deletions src/Autofac/Core/ISharingLifetimeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,38 @@ public interface ISharingLifetimeScope : ILifetimeScope
/// <returns><c>true</c> if the key was found; otherwise, <c>false</c>.</returns>
bool TryGetSharedInstance(Guid id, out object value);

/// <summary>
/// Try to retrieve a shared instance based on a primary GUID key and
/// possible secondary qualifying GUID key.
/// </summary>
/// <param name="primaryId">Key to look up.</param>
/// <param name="qualifyingId">
/// Secondary key to look up, to better identify an instance that wraps around another instance
/// or is otherwise "namespaced" by it.
/// </param>
/// <param name="value">The instance that has the specified keys.</param>
/// <returns><c>true</c> if the key was found; otherwise, <c>false</c>.</returns>
bool TryGetSharedInstance(Guid primaryId, Guid? qualifyingId, out object value);

/// <summary>
/// Creates a shared instance with a GUID key.
/// </summary>
/// <param name="id">Key.</param>
/// <param name="creator">A function that will create the instance when called.</param>
/// <returns>The shared instance.</returns>
object CreateSharedInstance(Guid id, Func<object> creator);

/// <summary>
/// Creates a shared instance with a primary GUID key and
/// possible secondary qualifying GUID key.
/// </summary>
/// <param name="primaryId">Key.</param>
/// <param name="qualifyingId">
/// Secondary key, to better identify an instance that wraps around another instance
/// or is otherwise "namespaced" by it.
/// </param>
/// <param name="creator">A function that will create the instance when called.</param>
/// <returns>The shared instance.</returns>
object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func<object> creator);
}
}
35 changes: 34 additions & 1 deletion src/Autofac/Core/Lifetime/LifetimeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class LifetimeScope : Disposable, ISharingLifetimeScope, IServiceProvider
/// </summary>
private readonly object _synchRoot = new object();
private readonly ConcurrentDictionary<Guid, object> _sharedInstances = new ConcurrentDictionary<Guid, object>();
private readonly ConcurrentDictionary<(Guid, Guid), object> _sharedQualifiedInstances = new ConcurrentDictionary<(Guid, Guid), object>();
private object? _anonymousTag;
private LifetimeScope? parentScope;

Expand Down Expand Up @@ -293,7 +294,6 @@ public object ResolveComponent(ResolveRequest request)
public object CreateSharedInstance(Guid id, Func<object> creator)
{
if (creator == null) throw new ArgumentNullException(nameof(creator));

lock (_synchRoot)
{
if (_sharedInstances.TryGetValue(id, out var result)) return result;
Expand All @@ -308,9 +308,42 @@ public object CreateSharedInstance(Guid id, Func<object> creator)
}
}

/// <inheritdoc/>
public object CreateSharedInstance(Guid primaryId, Guid? qualifyingId, Func<object> creator)
{
if (creator == null) throw new ArgumentNullException(nameof(creator));
if (qualifyingId == null)
{
return CreateSharedInstance(primaryId, creator);
}

lock (_synchRoot)
{
var instanceKey = (primaryId, qualifyingId.Value);

if (_sharedQualifiedInstances.TryGetValue(instanceKey, out var result)) return result;

result = creator();
if (_sharedQualifiedInstances.ContainsKey(instanceKey))
throw new DependencyResolutionException(string.Format(CultureInfo.CurrentCulture, LifetimeScopeResources.SelfConstructingDependencyDetected, result.GetType().FullName));

_sharedQualifiedInstances.TryAdd(instanceKey, result);

return result;
}
}

/// <inheritdoc />
public bool TryGetSharedInstance(Guid id, out object value) => _sharedInstances.TryGetValue(id, out value);

/// <inheritdoc/>
public bool TryGetSharedInstance(Guid primaryId, Guid? qualifyingId, out object value)
{
return qualifyingId == null
? TryGetSharedInstance(primaryId, out value)
: _sharedQualifiedInstances.TryGetValue((primaryId, qualifyingId.Value), out value);
}

/// <summary>
/// Gets the disposer associated with this container. Instances can be associated
/// with it manually if required.
Expand Down
4 changes: 2 additions & 2 deletions src/Autofac/Core/Resolving/InstanceLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ public object Execute()

var resolveParameters = Parameters as Parameter[] ?? Parameters.ToArray();

if (!_activationScope.TryGetSharedInstance(ComponentRegistration.Id, out var instance))
if (!_activationScope.TryGetSharedInstance(ComponentRegistration.Id, _decoratorTargetComponent?.Id, out var instance))
{
instance = sharing == InstanceSharing.Shared
? _activationScope.CreateSharedInstance(ComponentRegistration.Id, () => CreateInstance(Parameters))
? _activationScope.CreateSharedInstance(ComponentRegistration.Id, _decoratorTargetComponent?.Id, () => CreateInstance(Parameters))
: CreateInstance(Parameters);
}

Expand Down
10 changes: 5 additions & 5 deletions test/Autofac.Specification.Test/Features/DecoratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public void CanResolveMultipleDecoratedServicesWithMultipleDecorators()
});
}

[Fact(Skip = "Issue 1113")]
[Fact]
public void CanResolveMultipleDecoratedServicesSingleInstance()
{
var builder = new ContainerBuilder();
Expand Down Expand Up @@ -277,7 +277,7 @@ public void CanResolveMultipleDecoratedServicesSingleInstance()
});
}

[Fact(Skip = "Issue 1113")]
[Fact]
public void CanResolveMultipleDecoratedServicesWithMultipleDecoratorsSingleInstance()
{
var builder = new ContainerBuilder();
Expand Down Expand Up @@ -323,7 +323,7 @@ public void CanResolveMultipleDecoratedServicesWithMultipleDecoratorsSingleInsta
});
}

[Fact(Skip ="Issue 1113")]
[Fact]
public void CanResolveMultipleDecoratedServicesInstancePerLifetimeScope()
{
var builder = new ContainerBuilder();
Expand Down Expand Up @@ -384,7 +384,7 @@ public void CanResolveMultipleDecoratedServicesInstancePerLifetimeScope()
});
}

[Fact(Skip = "Issue 1113")]
[Fact]
public void CanResolveMultipleDecoratedServicesWithMultipleDecoratorsInstancePerLifetimeScope()
{
var builder = new ContainerBuilder();
Expand Down Expand Up @@ -514,7 +514,7 @@ public void CanResolveMultipleDecoratedServicesWithMultipleDecoratorsThenLatestS
Assert.IsType<ImplementorB>(service.Decorated.Decorated);
}

[Fact(Skip = "Issue 1113")]
[Fact]
public void CanResolveMultipleDecoratedServicesThenLatestServiceWithSingleInstance()
{
var builder = new ContainerBuilder();
Expand Down

0 comments on commit 67717d5

Please sign in to comment.