Skip to content

Commit

Permalink
Fixes #1077 - Keep raising 'Registered' and 'RegistrationSourceAdded'…
Browse files Browse the repository at this point in the history
… events after the 'ComponentRegistry' has been built

Co-authored-by: Alex Meyer-Gleaves <[email protected]>
  • Loading branch information
weelink and alexmg authored Feb 12, 2020
1 parent f55da62 commit e938b26
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/Autofac/Builder/MetadataKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ internal static class MetadataKeys
internal const string RegisteredPropertyKey = "__RegisteredKey";

internal const string RegistrationSourceAddedPropertyKey = "__RegistrationSourceAddedKey";

internal const string InternalRegisteredPropertyKey = "__InternalRegisteredKey";

internal const string InternalRegistrationSourceAddedPropertyKey = "__InternalRegistrationSourceAddedKey";
}
}
21 changes: 18 additions & 3 deletions src/Autofac/Core/Registration/ComponentRegistryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ internal ComponentRegistryBuilder(IRegisteredServicesTracker registeredServicesT
{
Properties = properties;
_registeredServicesTracker = registeredServicesTracker;
_registeredServicesTracker.Registered += OnRegistered;
_registeredServicesTracker.RegistrationSourceAdded += OnRegistrationSourceAdded;
}

private void OnRegistered(object sender, IComponentRegistration e)
{
var handler = GetRegistered();

handler?.Invoke(this, new ComponentRegisteredEventArgs(this, e));
}

private void OnRegistrationSourceAdded(object sender, IRegistrationSource e)
{
var handler = GetRegistrationSourceAdded();

handler?.Invoke(this, new RegistrationSourceAddedEventArgs(this, e));
}

/// <summary>
Expand All @@ -34,6 +50,8 @@ internal ComponentRegistryBuilder(IRegisteredServicesTracker registeredServicesT
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
_registeredServicesTracker.Registered -= OnRegistered;
_registeredServicesTracker.RegistrationSourceAdded -= OnRegistrationSourceAdded;
_registeredServicesTracker.Dispose();

base.Dispose(disposing);
Expand Down Expand Up @@ -76,7 +94,6 @@ public void Register(IComponentRegistration registration)
if (registration == null) throw new ArgumentNullException(nameof(registration));

_registeredServicesTracker.AddRegistration(registration, false);
GetRegistered()?.Invoke(this, new ComponentRegisteredEventArgs(this, registration));
}

/// <summary>
Expand All @@ -90,7 +107,6 @@ public void Register(IComponentRegistration registration, bool preserveDefaults)
if (registration == null) throw new ArgumentNullException(nameof(registration));

_registeredServicesTracker.AddRegistration(registration, preserveDefaults);
GetRegistered()?.Invoke(this, new ComponentRegisteredEventArgs(this, registration));
}

/// <summary>
Expand Down Expand Up @@ -122,7 +138,6 @@ public event EventHandler<ComponentRegisteredEventArgs> Registered
public void AddRegistrationSource(IRegistrationSource source)
{
_registeredServicesTracker.AddRegistrationSource(source);
GetRegistrationSourceAdded()?.Invoke(this, new RegistrationSourceAddedEventArgs(this, source));
}

/// <summary>
Expand Down
72 changes: 72 additions & 0 deletions src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,61 @@ internal class DefaultRegisteredServicesTracker : Disposable, IRegisteredService
private readonly ConcurrentDictionary<IServiceWithType, IReadOnlyList<IComponentRegistration>> _decorators
= new ConcurrentDictionary<IServiceWithType, IReadOnlyList<IComponentRegistration>>();

/// <summary>
/// Gets the set of properties used during component registration.
/// </summary>
/// <value>
/// An <see cref="IDictionary{TKey, TValue}"/> that can be used to share
/// context across registrations.
/// </value>
private readonly IDictionary<string, object?> _properties;

/// <summary>
/// Protects instance variables from concurrent access.
/// </summary>
private readonly object _synchRoot = new object();

/// <summary>
/// Initializes a new instance of the <see cref="DefaultRegisteredServicesTracker" /> class.
/// </summary>
public DefaultRegisteredServicesTracker()
{
_properties = new Dictionary<string, object?>();
}

/// <summary>
/// Fired whenever a component is registered - either explicitly or via a
/// <see cref="IRegistrationSource"/>.
/// </summary>
public event EventHandler<IComponentRegistration> Registered
{
add
{
_properties[MetadataKeys.InternalRegisteredPropertyKey] = GetRegistered() + value;
}

remove
{
_properties[MetadataKeys.InternalRegisteredPropertyKey] = GetRegistered() - value;
}
}

/// <summary>
/// Fired when an <see cref="IRegistrationSource"/> is added to the registry.
/// </summary>
public event EventHandler<IRegistrationSource> RegistrationSourceAdded
{
add
{
_properties[MetadataKeys.InternalRegistrationSourceAddedPropertyKey] = GetRegistrationSourceAdded() + value;
}

remove
{
_properties[MetadataKeys.InternalRegistrationSourceAddedPropertyKey] = GetRegistrationSourceAdded() - value;
}
}

/// <inheritdoc />
public IEnumerable<IComponentRegistration> Registrations
{
Expand Down Expand Up @@ -70,6 +120,7 @@ public virtual void AddRegistration(IComponentRegistration registration, bool pr
}

_registrations.Add(registration);
GetRegistered()?.Invoke(this, registration);
}

/// <inheritdoc />
Expand All @@ -82,6 +133,9 @@ public void AddRegistrationSource(IRegistrationSource source)
_dynamicRegistrationSources.Insert(0, source);
foreach (var serviceRegistrationInfo in _serviceInfo)
serviceRegistrationInfo.Value.Include(source);

var handler = GetRegistrationSourceAdded();
handler?.Invoke(this, source);
}
}

Expand Down Expand Up @@ -189,5 +243,23 @@ private ServiceRegistrationInfo GetServiceInfo(Service service)
_serviceInfo.TryAdd(service, info);
return info;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private EventHandler<IComponentRegistration>? GetRegistered()
{
if (_properties.TryGetValue(MetadataKeys.InternalRegisteredPropertyKey, out var registered))
return (EventHandler<IComponentRegistration>?)registered;

return null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private EventHandler<IRegistrationSource>? GetRegistrationSourceAdded()
{
if (_properties.TryGetValue(MetadataKeys.InternalRegistrationSourceAddedPropertyKey, out var registrationSourceAdded))
return (EventHandler<IRegistrationSource>?)registrationSourceAdded;

return null;
}
}
}
11 changes: 11 additions & 0 deletions src/Autofac/Core/Registration/IRegisteredServicesTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ internal interface IRegisteredServicesTracker : IDisposable
/// <returns>True if a registration exists.</returns>
bool TryGetRegistration(Service service, [NotNullWhen(returnValue: true)] out IComponentRegistration? registration);

/// <summary>
/// Fired whenever a component is registered - either explicitly or via a
/// <see cref="IRegistrationSource"/>.
/// </summary>
event EventHandler<IComponentRegistration> Registered;

/// <summary>
/// Fired when an <see cref="IRegistrationSource"/> is added to the registry.
/// </summary>
event EventHandler<IRegistrationSource> RegistrationSourceAdded;

/// <summary>
/// Gets the registered components.
/// </summary>
Expand Down
43 changes: 43 additions & 0 deletions test/Autofac.Test/ContainerBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

using Xunit;

Expand Down Expand Up @@ -41,5 +42,47 @@ public void RegistrationsCanUsePropertyBag()

Assert.Equal(2, container.ComponentRegistry.Properties["count"]);
}

[Fact]
public void WhenComponentIsRegisteredDuringResolveItShouldRaiseTheRegisteredEvent()
{
var activatedInstances = new List<object>();

var builder = new ContainerBuilder();
builder.RegisterCallback(x =>
x.Registered += (sender, args) =>
{
args.ComponentRegistration.Activating += (o, eventArgs) => activatedInstances.Add(eventArgs.Instance);
}
);

builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterType<Controller>().PropertiesAutowired();

IContainer container = builder.Build();
var controller = container.Resolve<Controller>();
controller.UseTheRepository();

Assert.Contains(activatedInstances, instance => instance is Controller);
Assert.Contains(activatedInstances, instance => instance is IRepository<object>);
}

public interface IRepository<T>
{
}

public class Repository<T> : IRepository<T>
{
}

public class Controller
{
public Lazy<IRepository<object>> TheRepository { get; set; }

public void UseTheRepository()
{
Assert.NotNull(TheRepository.Value);
}
}
}
}

0 comments on commit e938b26

Please sign in to comment.