Repository: autofac/Autofac

Issue:
`AutoActivate()` hides a registration's default "self" service
## Describe the Bug

A registration like `builder.Register<A>()` defaults to exposing the typed service `A` on the component.

Adding `AutoActivate()` to the registration hides this default. Since the name `AutoActivate()` doesn't suggest that it has anything to do with exposed services, this seems like a bug or rough edge in the API.

## Steps to Reproduce

```c#
public class StartableTests
{
    [Fact]
    public void AutoActivate_DoesNotHideDefaultSelfService()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyComponent2>().AutoActivate();
        using var container = builder.Build();
        Assert.True(container.IsRegistered<MyComponent2>());
    }
```

The test can be made to succeed by adding `AsSelf()` to the registration.

## Expected Behavior

`AutoActivate()` would not alter the services exposed by the component in a user-visible manner.

## Dependency Versions

`develop` branch.

## Additional Info

Happy to try to fix this and send a PR, just opening this first to gauge whether it's a known issue or by design. Not in any way blocked on this :-)

`AutoActivate()` hides a registration's default "self" service
## Describe the Bug

A registration like `builder.Register<A>()` defaults to exposing the typed service `A` on the component.

Adding `AutoActivate()` to the registration hides this default. Since the name `AutoActivate()` doesn't suggest that it has anything to do with exposed services, this seems like a bug or rough edge in the API.

## Steps to Reproduce

```c#
public class StartableTests
{
    [Fact]
    public void AutoActivate_DoesNotHideDefaultSelfService()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MyComponent2>().AutoActivate();
        using var container = builder.Build();
        Assert.True(container.IsRegistered<MyComponent2>());
    }
```

The test can be made to succeed by adding `AsSelf()` to the registration.

## Expected Behavior

`AutoActivate()` would not alter the services exposed by the component in a user-visible manner.

## Dependency Versions

`develop` branch.

## Additional Info

Happy to try to fix this and send a PR, just opening this first to gauge whether it's a known issue or by design. Not in any way blocked on this :-)



Hints:
I don't think it's by design, I'm guessing we just don't have a test for the issue mentioned. It'd be awesome to get a PR for it if you're able. Thanks!
I don't think it's by design, I'm guessing we just don't have a test for the issue mentioned. It'd be awesome to get a PR for it if you're able. Thanks!

Patch:
diff --git a/src/Autofac/Builder/RegistrationData.cs b/src/Autofac/Builder/RegistrationData.cs
index f14e6bcfb..4e5ba29bf 100644
--- a/src/Autofac/Builder/RegistrationData.cs
+++ b/src/Autofac/Builder/RegistrationData.cs
@@ -17,7 +17,7 @@ public class RegistrationData
     private bool _defaultServiceOverridden;
     private Service _defaultService;
 
-    private readonly ICollection<Service> _services = new HashSet<Service>();
+    private readonly HashSet<Service> _services = [];
 
     private IComponentLifetime _lifetime = CurrentScopeLifetime.Instance;
 
@@ -43,12 +43,15 @@ public IEnumerable<Service> Services
     {
         get
         {
-            if (_defaultServiceOverridden)
+            if (!_defaultServiceOverridden && !_services.Contains(_defaultService))
             {
-                return _services;
+                yield return _defaultService;
             }
 
-            return _services.DefaultIfEmpty(_defaultService);
+            foreach (var service in _services)
+            {
+                yield return service;
+            }
         }
     }
 
@@ -65,11 +68,15 @@ public void AddServices(IEnumerable<Service> services)
             throw new ArgumentNullException(nameof(services));
         }
 
-        _defaultServiceOverridden = true; // important even when services is empty
+        var empty = true;
         foreach (var service in services)
         {
+            empty = false;
             AddService(service);
         }
+
+        // `As([])` clears the default service.
+        _defaultServiceOverridden = _defaultServiceOverridden || empty;
     }
 
     /// <summary>
@@ -83,7 +90,9 @@ public void AddService(Service service)
             throw new ArgumentNullException(nameof(service));
         }
 
-        _defaultServiceOverridden = true;
+        // `AutoActivateService` is internal plumbing; `AutoActivate()` isn't expected to modify user-visible
+        // services.
+        _defaultServiceOverridden = _defaultServiceOverridden || service is not AutoActivateService;
         _services.Add(service);
     }
 
diff --git a/src/Autofac/RegistrationExtensions.cs b/src/Autofac/RegistrationExtensions.cs
index fb0c40b30..55b7e579f 100644
--- a/src/Autofac/RegistrationExtensions.cs
+++ b/src/Autofac/RegistrationExtensions.cs
@@ -69,6 +69,11 @@ public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationSty
 
         rb.SingleInstance();
 
+        // https://github.com/autofac/Autofac/issues/1102
+        // Single instance registrations with any custom activation phases (i.e. activation handlers) need to be auto-activated,
+        // so that other behavior (such as OnRelease) that expects 'normal' object lifetime behavior works as expected.
+        rb.RegistrationData.AddService(new AutoActivateService());
+
         rb.RegistrationData.DeferredCallback = builder.RegisterCallback(cr =>
         {
             if (rb.RegistrationData.Lifetime is not RootScopeLifetime ||
@@ -79,21 +84,6 @@ public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationSty
 
             activator.DisposeInstance = rb.RegistrationData.Ownership == InstanceOwnership.OwnedByLifetimeScope;
 
-            // https://github.com/autofac/Autofac/issues/1102
-            // Single instance registrations with any custom activation phases (i.e. activation handlers) need to be auto-activated,
-            // so that other behavior (such as OnRelease) that expects 'normal' object lifetime behavior works as expected.
-            if (rb.ResolvePipeline.Middleware.Any(s => s.Phase == PipelinePhase.Activation))
-            {
-                var autoStartService = rb.RegistrationData.Services.First();
-
-                var activationRegistration = new RegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle>(
-                    new AutoActivateService(),
-                    new SimpleActivatorData(new DelegateActivator(typeof(T), (c, p) => c.ResolveService(autoStartService))),
-                    new SingleRegistrationStyle());
-
-                RegistrationBuilder.RegisterSingleComponent(cr, activationRegistration);
-            }
-
             RegistrationBuilder.RegisterSingleComponent(cr, rb);
         });
 


Test patch:
diff --git a/test/Autofac.Specification.Test/Features/StartableTests.cs b/test/Autofac.Specification.Test/Features/StartableTests.cs
index ed3b1ee71..e993e9785 100644
--- a/test/Autofac.Specification.Test/Features/StartableTests.cs
+++ b/test/Autofac.Specification.Test/Features/StartableTests.cs
@@ -168,6 +168,26 @@ public void Startable_WhenTheContainerIsBuilt_StartableComponentsThatDependOnAut
         Assert.Equal(expectedStartCount, StartableDependency.Count);
     }
 
+    [Fact]
+    public void AutoActivate_DoesNotHideDefaultSelfService()
+    {
+        var builder = new ContainerBuilder();
+        builder.RegisterType<MyComponent2>().AutoActivate();
+        using var container = builder.Build();
+        Assert.True(container.IsRegistered<MyComponent2>());
+    }
+
+    [Fact]
+    public void AutoActivate_RegisterInstanceActivationWorksWhenDefaultServiceOverloaded()
+    {
+        var instanceCount = 0;
+        var builder = new ContainerBuilder();
+        builder.RegisterInstance(new MyComponent2()).As<object>().OnActivated(_ => instanceCount++);
+        builder.RegisterType<object>();
+        builder.Build();
+        Assert.Equal(1, instanceCount);
+    }
+
     private class ComponentTakesStartableDependency : IStartable
     {
         public ComponentTakesStartableDependency(StartableTakesDependency dependency, bool expectStarted)