Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bench/Autofac.Benchmarks/BenchmarkSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class BenchmarkSet
typeof(ConcurrencyBenchmark),
typeof(ConcurrencyNestedScopeBenchmark),
typeof(KeyedGenericBenchmark),
typeof(KeyedAnyKeySimpleBenchmark),
typeof(KeyedNestedBenchmark),
typeof(KeyedSimpleBenchmark),
typeof(KeylessGenericBenchmark),
Expand Down
2 changes: 0 additions & 2 deletions bench/Autofac.Benchmarks/Decorators/DecoratorBenchmarkBase.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Autofac.Benchmarks.Decorators;

public abstract class DecoratorBenchmarkBase<TCommandHandler>
Expand Down
29 changes: 29 additions & 0 deletions bench/Autofac.Benchmarks/Decorators/KeyedAnyKeySimpleBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Autofac.Benchmarks.Decorators.Scenario;
using Autofac.Core;

namespace Autofac.Benchmarks.Decorators;

/// <summary>
/// Benchmarks keyed decorators when components are registered with AnyKey.
/// </summary>
public class KeyedAnyKeySimpleBenchmark : DecoratorBenchmarkBase<ICommandHandler>
{
[GlobalSetup]
public void Setup()
{
var builder = new ContainerBuilder();

builder.RegisterType<CommandHandlerOne>()
.Keyed<ICommandHandler>(KeyedService.AnyKey);
builder.RegisterType<CommandHandlerTwo>()
.Keyed<ICommandHandler>(KeyedService.AnyKey);
builder.RegisterDecorator<ICommandHandler>(
(c, inner) => new CommandHandlerDecoratorOne(inner),
fromKey: "handler");

Container = builder.Build();
}
}
28 changes: 17 additions & 11 deletions src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ private void UseSingleConstructorActivation(IResolvePipelineBuilder pipelineBuil

var instance = boundConstructor.Instantiate();

InjectProperties(instance, context, boundConstructor, GetAllParameters(context.Parameters));
if (ShouldInjectProperties(boundConstructor))
{
var prioritizedParameters = GetAllParameters(context.Parameters);
InjectProperties(instance, context, boundConstructor, prioritizedParameters);
}

context.Instance = instance;

Expand Down Expand Up @@ -422,20 +426,12 @@ private string GetBindingFailureMessage(BoundConstructor[] constructorBindings)

private void InjectProperties(object instance, IComponentContext context, BoundConstructor constructor, IEnumerable<Parameter> allParameters)
{
// We only need to do any injection if we have a set of property information.
if (_defaultFoundPropertySet is null)
{
return;
}

// If this constructor sets all required members,
// and we have no configured properties, we can just jump out.
if (_configuredProperties.Length == 0 && constructor.SetsRequiredMembers)
if (!ShouldInjectProperties(constructor))
{
return;
}

var workingSetOfProperties = (InjectablePropertyState[])_defaultFoundPropertySet.Clone();
var workingSetOfProperties = (InjectablePropertyState[])_defaultFoundPropertySet!.Clone();

foreach (var configuredProperty in _configuredProperties)
{
Expand Down Expand Up @@ -509,6 +505,16 @@ private void InjectProperties(object instance, IComponentContext context, BoundC
}
}

private bool ShouldInjectProperties(BoundConstructor constructor)
{
if (_defaultFoundPropertySet is null)
{
return false;
}

return _configuredProperties.Length != 0 || !constructor.SetsRequiredMembers;
}

private string BuildRequiredPropertyResolutionMessage(IReadOnlyList<InjectableProperty> failingRequiredProperties)
{
var propertyDescriptions = new StringBuilder();
Expand Down
15 changes: 15 additions & 0 deletions src/Autofac/Core/KeyedServiceParameterInjector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,21 @@ private static IEnumerable<Parameter> AppendKeyParameter(IEnumerable<Parameter>
return new Parameter[] { keyParameter };
}

// Build a concrete array to avoid LINQ AppendIterator allocation.
if (parameters is IReadOnlyCollection<Parameter> collection)
{
var result = new Parameter[collection.Count + 1];
var i = 0;
foreach (var p in collection)
{
result[i++] = p;
}

result[i] = keyParameter;
return result;
}

// Fallback for unknown enumerable types.
return parameters.Append(keyParameter);
}
}
1 change: 0 additions & 1 deletion src/Autofac/Core/Lifetime/LifetimeScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Threading;
#if NET5_0_OR_GREATER
using System.Runtime.Loader;
#endif
Expand Down
45 changes: 45 additions & 0 deletions src/Autofac/Core/ReflectionCacheSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public sealed class ReflectionCacheSet

private readonly ConcurrentDictionary<string, IReflectionCache> _caches = new();

private readonly List<WeakReference<IReflectionCache>> _externalCaches = new();

/// <summary>
/// Initializes a new instance of the <see cref="ReflectionCacheSet"/> class.
/// </summary>
Expand Down Expand Up @@ -101,6 +103,26 @@ public TCacheStore GetOrCreateCache<TCacheStore>(string cacheName, Func<string,
}
}

/// <summary>
/// Register an externally-owned <see cref="IReflectionCache"/> so it participates
/// in <see cref="Clear()"/> and <see cref="Clear(ReflectionCacheClearPredicate)"/> calls.
/// The cache is held via a weak reference so it does not prevent garbage collection
/// of the owning object (e.g., a container or registration source).
/// </summary>
/// <param name="cache">The cache to register.</param>
public void RegisterExternalCache(IReflectionCache cache)
{
if (cache is null)
{
throw new ArgumentNullException(nameof(cache));
}

lock (_externalCaches)
{
_externalCaches.Add(new WeakReference<IReflectionCache>(cache));
}
}

/// <summary>
/// Clear the internal reflection cache. Only call this method if you are
/// dynamically unloading types from the process; calling this method
Expand All @@ -117,6 +139,8 @@ public void Clear()
{
cache.Clear();
}

ClearExternalCaches(static cache => cache.Clear());
}

/// <summary>
Expand All @@ -139,6 +163,8 @@ public void Clear(ReflectionCacheClearPredicate predicate)
{
cache.Clear(predicate);
}

ClearExternalCaches(cache => cache.Clear(predicate));
}

/// <summary>
Expand Down Expand Up @@ -173,6 +199,25 @@ private static bool TryGetSharedCache([NotNullWhen(true)] out ReflectionCacheSet
return _sharedSet.TryGetTarget(out sharedCache);
}

private void ClearExternalCaches(Action<IReflectionCache> clearAction)
{
lock (_externalCaches)
{
for (var i = _externalCaches.Count - 1; i >= 0; i--)
{
if (_externalCaches[i].TryGetTarget(out var cache))
{
clearAction(cache);
}
else
{
// Prune dead references.
_externalCaches.RemoveAt(i);
}
}
}
}

private IEnumerable<IReflectionCache> GetAllCaches()
{
foreach (var externalItem in _caches)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Globalization;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Diagnostics;

namespace Autofac.Core.Resolving.Middleware;

Expand All @@ -29,20 +28,23 @@ private ActivatorErrorHandlingMiddleware()
/// <inheritdoc />
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
if (!AutofacMetrics.MetricsEnabled)
try
{
ExecuteCore(context, next);
return;
}
next(context);

var timer = ValueStopwatch.StartNew();
try
if (context.Instance is null)
{
// Exited the Activation Stage without creating an instance.
throw new DependencyResolutionException(MiddlewareMessages.ActivatorDidNotPopulateInstance);
}
}
catch (ObjectDisposedException)
{
ExecuteCore(context, next);
throw;
}
finally
catch (Exception ex)
{
AutofacMetrics.RecordMiddlewareExecution(nameof(ActivatorErrorHandlingMiddleware), timer.GetElapsedTime());
throw PropagateActivationException(context.Registration.Activator, ex);
}
}

Expand All @@ -65,26 +67,4 @@ private static DependencyResolutionException PropagateActivationException(IInsta
result.Data[ActivatorChainExceptionData] = activatorChain;
return result;
}

private static void ExecuteCore(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
try
{
next(context);

if (context.Instance is null)
{
// Exited the Activation Stage without creating an instance.
throw new DependencyResolutionException(MiddlewareMessages.ActivatorDidNotPopulateInstance);
}
}
catch (ObjectDisposedException)
{
throw;
}
catch (Exception ex)
{
throw PropagateActivationException(context.Registration.Activator, ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Diagnostics;

namespace Autofac.Core.Resolving.Middleware;

Expand Down Expand Up @@ -39,51 +38,6 @@ public CircularDependencyDetectorMiddleware(int maxResolveDepth)

/// <inheritdoc/>
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
if (!AutofacMetrics.MetricsEnabled)
{
ExecuteCore(context, next);
return;
}

var timer = ValueStopwatch.StartNew();
try
{
ExecuteCore(context, next);
}
finally
{
AutofacMetrics.RecordMiddlewareExecution(nameof(CircularDependencyDetectorMiddleware), timer.GetElapsedTime());
}
}

/// <inheritdoc/>
public override string ToString() => nameof(CircularDependencyDetectorMiddleware);

private static string CreateDependencyGraphTo(IComponentRegistration registration, IEnumerable<ResolveRequestContext> requestStack)
{
if (registration == null)
{
throw new ArgumentNullException(nameof(registration));
}

if (requestStack == null)
{
throw new ArgumentNullException(nameof(requestStack));
}

var dependencyGraph = Display(registration);

return requestStack.Select(a => a.Registration)
.Aggregate(dependencyGraph, (current, requestor) => Display(requestor) + " -> " + current);
}

private static string Display(IComponentRegistration registration)
{
return registration.Activator.DisplayName();
}

private void ExecuteCore(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
if (context.Operation is not IDependencyTrackingResolveOperation dependencyTrackingResolveOperation)
{
Expand Down Expand Up @@ -141,4 +95,30 @@ private void ExecuteCore(ResolveRequestContext context, Action<ResolveRequestCon
requestStack.Pop();
}
}

/// <inheritdoc/>
public override string ToString() => nameof(CircularDependencyDetectorMiddleware);

private static string CreateDependencyGraphTo(IComponentRegistration registration, IEnumerable<ResolveRequestContext> requestStack)
{
if (registration == null)
{
throw new ArgumentNullException(nameof(registration));
}

if (requestStack == null)
{
throw new ArgumentNullException(nameof(requestStack));
}

var dependencyGraph = Display(registration);

return requestStack.Select(a => a.Registration)
.Aggregate(dependencyGraph, (current, requestor) => Display(requestor) + " -> " + current);
}

private static string Display(IComponentRegistration registration)
{
return registration.Activator.DisplayName();
}
}
17 changes: 1 addition & 16 deletions src/Autofac/Core/Resolving/Middleware/CoreEventMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Autofac.Core.Resolving.Pipeline;
using Autofac.Diagnostics;

namespace Autofac.Core.Resolving.Middleware;

Expand Down Expand Up @@ -40,20 +39,6 @@ internal CoreEventMiddleware(ResolveEventType eventType, PipelinePhase phase, Ac
/// <inheritdoc/>
public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
{
if (!AutofacMetrics.MetricsEnabled)
{
_callback(context, next);
return;
}

var timer = ValueStopwatch.StartNew();
try
{
_callback(context, next);
}
finally
{
AutofacMetrics.RecordMiddlewareExecution(ToString(), timer.GetElapsedTime());
}
_callback(context, next);
}
}
Loading
Loading