diff --git a/src/Autofac/Builder/MetadataKeys.cs b/src/Autofac/Builder/MetadataKeys.cs index d1dfe7135..64aa88907 100644 --- a/src/Autofac/Builder/MetadataKeys.cs +++ b/src/Autofac/Builder/MetadataKeys.cs @@ -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"; } } diff --git a/src/Autofac/Core/Registration/ComponentRegistryBuilder.cs b/src/Autofac/Core/Registration/ComponentRegistryBuilder.cs index 4637f71c2..367a4d440 100644 --- a/src/Autofac/Core/Registration/ComponentRegistryBuilder.cs +++ b/src/Autofac/Core/Registration/ComponentRegistryBuilder.cs @@ -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)); } /// @@ -34,6 +50,8 @@ internal ComponentRegistryBuilder(IRegisteredServicesTracker registeredServicesT /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { + _registeredServicesTracker.Registered -= OnRegistered; + _registeredServicesTracker.RegistrationSourceAdded -= OnRegistrationSourceAdded; _registeredServicesTracker.Dispose(); base.Dispose(disposing); @@ -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)); } /// @@ -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)); } /// @@ -122,7 +138,6 @@ public event EventHandler Registered public void AddRegistrationSource(IRegistrationSource source) { _registeredServicesTracker.AddRegistrationSource(source); - GetRegistrationSourceAdded()?.Invoke(this, new RegistrationSourceAddedEventArgs(this, source)); } /// diff --git a/src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs b/src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs index 8cd71cb2f..bea72c7d6 100644 --- a/src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs +++ b/src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs @@ -33,11 +33,61 @@ internal class DefaultRegisteredServicesTracker : Disposable, IRegisteredService private readonly ConcurrentDictionary> _decorators = new ConcurrentDictionary>(); + /// + /// Gets the set of properties used during component registration. + /// + /// + /// An that can be used to share + /// context across registrations. + /// + private readonly IDictionary _properties; + /// /// Protects instance variables from concurrent access. /// private readonly object _synchRoot = new object(); + /// + /// Initializes a new instance of the class. + /// + public DefaultRegisteredServicesTracker() + { + _properties = new Dictionary(); + } + + /// + /// Fired whenever a component is registered - either explicitly or via a + /// . + /// + public event EventHandler Registered + { + add + { + _properties[MetadataKeys.InternalRegisteredPropertyKey] = GetRegistered() + value; + } + + remove + { + _properties[MetadataKeys.InternalRegisteredPropertyKey] = GetRegistered() - value; + } + } + + /// + /// Fired when an is added to the registry. + /// + public event EventHandler RegistrationSourceAdded + { + add + { + _properties[MetadataKeys.InternalRegistrationSourceAddedPropertyKey] = GetRegistrationSourceAdded() + value; + } + + remove + { + _properties[MetadataKeys.InternalRegistrationSourceAddedPropertyKey] = GetRegistrationSourceAdded() - value; + } + } + /// public IEnumerable Registrations { @@ -70,6 +120,7 @@ public virtual void AddRegistration(IComponentRegistration registration, bool pr } _registrations.Add(registration); + GetRegistered()?.Invoke(this, registration); } /// @@ -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); } } @@ -189,5 +243,23 @@ private ServiceRegistrationInfo GetServiceInfo(Service service) _serviceInfo.TryAdd(service, info); return info; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private EventHandler? GetRegistered() + { + if (_properties.TryGetValue(MetadataKeys.InternalRegisteredPropertyKey, out var registered)) + return (EventHandler?)registered; + + return null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private EventHandler? GetRegistrationSourceAdded() + { + if (_properties.TryGetValue(MetadataKeys.InternalRegistrationSourceAddedPropertyKey, out var registrationSourceAdded)) + return (EventHandler?)registrationSourceAdded; + + return null; + } } } \ No newline at end of file diff --git a/src/Autofac/Core/Registration/IRegisteredServicesTracker.cs b/src/Autofac/Core/Registration/IRegisteredServicesTracker.cs index 76a2ed3fb..2004eff6d 100644 --- a/src/Autofac/Core/Registration/IRegisteredServicesTracker.cs +++ b/src/Autofac/Core/Registration/IRegisteredServicesTracker.cs @@ -31,6 +31,17 @@ internal interface IRegisteredServicesTracker : IDisposable /// True if a registration exists. bool TryGetRegistration(Service service, [NotNullWhen(returnValue: true)] out IComponentRegistration? registration); + /// + /// Fired whenever a component is registered - either explicitly or via a + /// . + /// + event EventHandler Registered; + + /// + /// Fired when an is added to the registry. + /// + event EventHandler RegistrationSourceAdded; + /// /// Gets the registered components. /// diff --git a/test/Autofac.Test/ContainerBuilderTests.cs b/test/Autofac.Test/ContainerBuilderTests.cs index 5954cda64..fbbb83b1b 100644 --- a/test/Autofac.Test/ContainerBuilderTests.cs +++ b/test/Autofac.Test/ContainerBuilderTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Xunit; @@ -41,5 +42,47 @@ public void RegistrationsCanUsePropertyBag() Assert.Equal(2, container.ComponentRegistry.Properties["count"]); } + + [Fact] + public void WhenComponentIsRegisteredDuringResolveItShouldRaiseTheRegisteredEvent() + { + var activatedInstances = new List(); + + 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().PropertiesAutowired(); + + IContainer container = builder.Build(); + var controller = container.Resolve(); + controller.UseTheRepository(); + + Assert.Contains(activatedInstances, instance => instance is Controller); + Assert.Contains(activatedInstances, instance => instance is IRepository); + } + + public interface IRepository + { + } + + public class Repository : IRepository + { + } + + public class Controller + { + public Lazy> TheRepository { get; set; } + + public void UseTheRepository() + { + Assert.NotNull(TheRepository.Value); + } + } } } \ No newline at end of file