diff --git a/Directory.Build.targets b/Directory.Build.targets
index b1fa0d3557e..ea8fb542103 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -33,7 +33,7 @@
$(NoWarn);AD0001
- $(NoWarn);EXTEXP0001;EXTEXP0002;EXTEXP0003;EXTEXP0004;EXTEXP0005;EXTEXP0006;EXTEXP0007;EXTEXP0008;EXTEXP0009;EXTEXP0010;EXTEXP0011
+ $(NoWarn);EXTEXP0001;EXTEXP0002;EXTEXP0003;EXTEXP0004;EXTEXP0005;EXTEXP0006;EXTEXP0007;EXTEXP0008;EXTEXP0009;EXTEXP0010;EXTEXP0011;EXTEXP0012
$(NoWarn);NU5104
diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md
index 09a85bdfba6..3e2f6cd3653 100644
--- a/docs/list-of-diagnostics.md
+++ b/docs/list-of-diagnostics.md
@@ -25,4 +25,5 @@ if desired.
| `EXTEXP0008` | Resource monitoring experiments |
| `EXTEXP0009` | Hosting experiments |
| `EXTEXP0010` | Object pool experiments |
-
+| `EXTEXP0011` | Document database experiments |
+| `EXTEXP0012` | Auto-activation experiments |
diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.Keyed.cs b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.Keyed.cs
new file mode 100644
index 00000000000..efb2f5c76fa
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.Keyed.cs
@@ -0,0 +1,386 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+public static partial class AutoActivationExtensions
+{
+ ///
+ /// Enforces keyed singleton activation at startup time rather then at runtime.
+ ///
+ /// The type of the service to activate.
+ /// The service collection containing the service.
+ /// An object used to uniquely identity the specific service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection ActivateKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ _ = Throw.IfNull(services);
+
+ _ = services
+ .AddHostedService()
+ .AddOptions()
+ .Configure(ao =>
+ {
+ var constructed = typeof(IEnumerable);
+ if (ao.KeyedAutoActivators.Contains((constructed, serviceKey)))
+ {
+ return;
+ }
+
+ if (ao.KeyedAutoActivators.Remove((typeof(TService), serviceKey)))
+ {
+ _ = ao.KeyedAutoActivators.Add((constructed, serviceKey));
+ return;
+ }
+
+ _ = ao.KeyedAutoActivators.Add((typeof(TService), serviceKey));
+ });
+
+ return services;
+ }
+
+ ///
+ /// Enforces keyed singleton activation at startup time rather then at runtime.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to activate.
+ /// An object used to uniquely identity the specific service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ [UnconditionalSuppressMessage(
+ "Trimming",
+ "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
+ Justification = "Addressed with [DynamicallyAccessedMembers]")]
+ public static IServiceCollection ActivateKeyedSingleton(
+ this IServiceCollection services,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType,
+ object? serviceKey)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+
+ _ = services
+ .AddHostedService()
+ .AddOptions()
+ .Configure(ao =>
+ {
+ var constructed = typeof(IEnumerable<>).MakeGenericType(serviceType);
+ if (ao.KeyedAutoActivators.Contains((constructed, serviceKey)))
+ {
+ return;
+ }
+
+ if (ao.KeyedAutoActivators.Remove((serviceType, serviceKey)))
+ {
+ _ = ao.KeyedAutoActivators.Add((constructed, serviceKey));
+ return;
+ }
+
+ _ = ao.KeyedAutoActivators.Add((serviceType, serviceKey));
+ });
+
+ return services;
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The factory that creates the service.
+ /// The type of the service to add.
+ /// The type of the implementation to use.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(implementationFactory);
+
+ return services
+ .AddKeyedSingleton(serviceKey, implementationFactory)
+ .ActivateKeyedSingleton(serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The type of the service to add.
+ /// The type of the implementation to use.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ return services
+ .AddKeyedSingleton(serviceKey)
+ .ActivateKeyedSingleton(serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The factory that creates the service.
+ /// The type of the service to add.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func implementationFactory)
+ where TService : class
+ {
+ return services
+ .AddKeyedSingleton(serviceKey, implementationFactory)
+ .ActivateKeyedSingleton(serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The type of the service to add.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ return services
+ .AddKeyedSingleton(serviceKey)
+ .ActivateKeyedSingleton(serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register and the implementation to use.
+ /// An object used to uniquely identity the specific service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+
+ return services
+ .AddKeyedSingleton(serviceType, serviceKey)
+ .ActivateKeyedSingleton(serviceType, serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register.
+ /// An object used to uniquely identity the specific service.
+ /// The factory that creates the service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Func implementationFactory)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+ _ = Throw.IfNull(implementationFactory);
+
+ return services
+ .AddKeyedSingleton(serviceType, serviceKey, implementationFactory)
+ .ActivateKeyedSingleton(serviceType, serviceKey);
+ }
+
+ ///
+ /// Adds an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register.
+ /// An object used to uniquely identity the specific service.
+ /// The implementation type of the service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection AddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Type implementationType)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+ _ = Throw.IfNull(implementationType);
+
+ return services
+ .AddKeyedSingleton(serviceType, serviceKey, implementationType)
+ .ActivateKeyedSingleton(serviceType, serviceKey);
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register.
+ /// An object used to uniquely identity the specific service.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceType, serviceKey, serviceType));
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register.
+ /// An object used to uniquely identity the specific service.
+ /// The implementation type of the service.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Type implementationType)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+ _ = Throw.IfNull(implementationType);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceType, serviceKey, implementationType));
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// The type of the service to register.
+ /// An object used to uniquely identity the specific service.
+ /// The factory that creates the service.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Func implementationFactory)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+ _ = Throw.IfNull(implementationFactory);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceType, serviceKey, implementationFactory));
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The type of the service to add.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ _ = Throw.IfNull(services);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceKey));
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The type of the service to add.
+ /// The type of the implementation to use.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ _ = Throw.IfNull(services);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceKey));
+ }
+
+ ///
+ /// Tries to add an auto-activated keyed singleton service.
+ ///
+ /// The service collection to add the service to.
+ /// An object used to uniquely identity the specific service.
+ /// The factory that creates the service.
+ /// The type of the service to add.
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static void TryAddActivatedKeyedSingleton(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func implementationFactory)
+ where TService : class
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(implementationFactory);
+
+ services.TryAddAndActivateKeyed(ServiceDescriptor.KeyedSingleton(serviceKey, implementationFactory));
+ }
+
+ private static void TryAddAndActivateKeyed(this IServiceCollection services, ServiceDescriptor descriptor)
+ where TService : class
+ {
+ if (services.Any(d => d.ServiceType == descriptor.ServiceType && d.ServiceKey == descriptor.ServiceKey))
+ {
+ return;
+ }
+
+ services.Add(descriptor);
+ _ = services.ActivateKeyedSingleton(descriptor.ServiceKey);
+ }
+
+ private static void TryAddAndActivateKeyed(this IServiceCollection services, ServiceDescriptor descriptor)
+ {
+ if (services.Any(d => d.ServiceType == descriptor.ServiceType && d.ServiceKey == descriptor.ServiceKey))
+ {
+ return;
+ }
+
+ services.Add(descriptor);
+ _ = services.ActivateKeyedSingleton(descriptor.ServiceType, descriptor.ServiceKey);
+ }
+}
diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.cs b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.cs
index 8230443df71..3f276efd229 100644
--- a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationExtensions.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Extensions.DependencyInjection;
@@ -13,51 +14,90 @@ namespace Microsoft.Extensions.DependencyInjection;
///
/// Extension methods for automatically activating singletons after application starts.
///
-public static class AutoActivationExtensions
+public static partial class AutoActivationExtensions
{
///
/// Enforces singleton activation at startup time rather then at runtime.
///
- /// The type of the service to add.
- /// The to add the service to.
- /// A reference to this instance after the operation has completed.
- public static IServiceCollection Activate(this IServiceCollection services)
+ /// The type of the service to activate.
+ /// The service collection containing the service.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ public static IServiceCollection ActivateSingleton(this IServiceCollection services)
where TService : class
{
_ = Throw.IfNull(services);
- _ = services.AddHostedService()
- .AddOptions()
- .Configure(ao =>
- {
- var constructed = typeof(IEnumerable);
- if (ao.AutoActivators.Contains(constructed))
- {
- return;
- }
-
- if (ao.AutoActivators.Remove(typeof(TService)))
- {
- _ = ao.AutoActivators.Add(constructed);
- return;
- }
-
- _ = ao.AutoActivators.Add(typeof(TService));
- });
+ _ = services
+ .AddHostedService()
+ .AddOptions()
+ .Configure(ao =>
+ {
+ var constructed = typeof(IEnumerable);
+ if (ao.AutoActivators.Contains(constructed))
+ {
+ return;
+ }
+
+ if (ao.AutoActivators.Remove(typeof(TService)))
+ {
+ _ = ao.AutoActivators.Add(constructed);
+ return;
+ }
+
+ _ = ao.AutoActivators.Add(typeof(TService));
+ });
+
+ return services;
+ }
+
+ ///
+ /// Enforces singleton activation at startup time rather then at runtime.
+ ///
+ /// The service collection containing the service.
+ /// The type of the service to activate.
+ /// The value of .
+ [Experimental(diagnosticId: Experiments.AutoActivation, UrlFormat = Experiments.UrlFormat)]
+ [UnconditionalSuppressMessage(
+ "Trimming",
+ "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
+ Justification = "Addressed with [DynamicallyAccessedMembers]")]
+ public static IServiceCollection ActivateSingleton(this IServiceCollection services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType)
+ {
+ _ = Throw.IfNull(services);
+ _ = Throw.IfNull(serviceType);
+
+ _ = services
+ .AddHostedService()
+ .AddOptions()
+ .Configure(ao =>
+ {
+ var constructed = typeof(IEnumerable<>).MakeGenericType(serviceType);
+ if (ao.AutoActivators.Contains(constructed))
+ {
+ return;
+ }
+
+ if (ao.AutoActivators.Remove(serviceType))
+ {
+ _ = ao.AutoActivators.Add(constructed);
+ return;
+ }
+
+ _ = ao.AutoActivators.Add(serviceType);
+ });
return services;
}
///
- /// Adds an auto-activated singleton service of the type specified in TService with an implementation
- /// type specified in TImplementation using the factory specified in implementationFactory
- /// to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The factory that creates the service.
/// The type of the service to add.
/// The type of the implementation to use.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services, Func implementationFactory)
where TService : class
where TImplementation : class, TService
@@ -67,63 +107,61 @@ public static IServiceCollection AddActivatedSingleton(implementationFactory)
- .Activate();
+ .ActivateSingleton();
}
///
- /// Adds an auto-activated singleton service of the type specified in TService with an implementation
- /// type specified in TImplementation to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to add.
/// The type of the implementation to use.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
return services
.AddSingleton()
- .Activate();
+ .ActivateSingleton();
}
///
- /// Adds an auto-activated singleton service of the type specified in TService with a factory specified
- /// in implementationFactory to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The factory that creates the service.
/// The type of the service to add.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services, Func implementationFactory)
where TService : class
{
return services
.AddSingleton(implementationFactory)
- .Activate();
+ .ActivateSingleton();
}
///
- /// Adds an auto-activated singleton service of the type specified in TService to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to add.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services)
where TService : class
{
return services
.AddSingleton()
- .Activate();
+ .ActivateSingleton();
}
///
/// Adds an auto-activated singleton service of the type specified in serviceType to the specified
/// .
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register and the implementation to use.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services, Type serviceType)
{
_ = Throw.IfNull(services);
@@ -131,17 +169,16 @@ public static IServiceCollection AddActivatedSingleton(this IServiceCollection s
return services
.AddSingleton(serviceType)
- .Activate(serviceType);
+ .ActivateSingleton(serviceType);
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType with a factory
- /// specified in implementationFactory to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register.
/// The factory that creates the service.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services, Type serviceType, Func implementationFactory)
{
_ = Throw.IfNull(services);
@@ -150,17 +187,16 @@ public static IServiceCollection AddActivatedSingleton(this IServiceCollection s
return services
.AddSingleton(serviceType, implementationFactory)
- .Activate(serviceType);
+ .ActivateSingleton(serviceType);
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType with an implementation
- /// of the type specified in implementationType to the specified .
+ /// Adds an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register.
/// The implementation type of the service.
- /// A reference to this instance after the operation has completed.
+ /// The value of .
public static IServiceCollection AddActivatedSingleton(this IServiceCollection services, Type serviceType, Type implementationType)
{
_ = Throw.IfNull(services);
@@ -169,14 +205,13 @@ public static IServiceCollection AddActivatedSingleton(this IServiceCollection s
return services
.AddSingleton(serviceType, implementationType)
- .Activate(serviceType);
+ .ActivateSingleton(serviceType);
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register.
public static void TryAddActivatedSingleton(this IServiceCollection services, Type serviceType)
{
@@ -187,11 +222,9 @@ public static void TryAddActivatedSingleton(this IServiceCollection services, Ty
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType with an implementation
- /// of the type specified in implementationType to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register.
/// The implementation type of the service.
public static void TryAddActivatedSingleton(this IServiceCollection services, Type serviceType, Type implementationType)
@@ -204,11 +237,9 @@ public static void TryAddActivatedSingleton(this IServiceCollection services, Ty
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType with a factory
- /// specified in implementationFactory to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to register.
/// The factory that creates the service.
public static void TryAddActivatedSingleton(this IServiceCollection services, Type serviceType, Func implementationFactory)
@@ -221,11 +252,9 @@ public static void TryAddActivatedSingleton(this IServiceCollection services, Ty
}
///
- /// Adds an auto-activated singleton service of the type specified in TService
- /// to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to add.
public static void TryAddActivatedSingleton(this IServiceCollection services)
where TService : class
@@ -236,12 +265,9 @@ public static void TryAddActivatedSingleton(this IServiceCollection se
}
///
- /// Adds an auto-activated singleton service of the type specified in TService with an implementation
- /// type specified in TImplementation using the factory specified in implementationFactory
- /// to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The type of the service to add.
/// The type of the implementation to use.
public static void TryAddActivatedSingleton(this IServiceCollection services)
@@ -254,11 +280,9 @@ public static void TryAddActivatedSingleton(this ISer
}
///
- /// Adds an auto-activated singleton service of the type specified in serviceType with a factory
- /// specified in implementationFactory to the specified
- /// if the service type hasn't already been registered.
+ /// Tries to add an auto-activated singleton service.
///
- /// The to add the service to.
+ /// The service collection to add the service to.
/// The factory that creates the service.
/// The type of the service to add.
public static void TryAddActivatedSingleton(this IServiceCollection services, Func implementationFactory)
@@ -270,54 +294,26 @@ public static void TryAddActivatedSingleton(this IServiceCollection se
services.TryAddAndActivate(ServiceDescriptor.Singleton(implementationFactory));
}
- [UnconditionalSuppressMessage(
- "Trimming",
- "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
- Justification = "Addressed with [DynamicallyAccessedMembers]")]
- internal static IServiceCollection Activate(this IServiceCollection services, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType)
- {
- _ = services.AddHostedService()
- .AddOptions()
- .Configure(ao =>
- {
- var constructed = typeof(IEnumerable<>).MakeGenericType(serviceType);
- if (ao.AutoActivators.Contains(constructed))
- {
- return;
- }
-
- if (ao.AutoActivators.Remove(serviceType))
- {
- _ = ao.AutoActivators.Add(constructed);
- return;
- }
-
- _ = ao.AutoActivators.Add(serviceType);
- });
-
- return services;
- }
-
private static void TryAddAndActivate(this IServiceCollection services, ServiceDescriptor descriptor)
where TService : class
{
- if (services.Any(d => d.ServiceType == descriptor.ServiceType))
+ if (services.Any(d => d.ServiceType == descriptor.ServiceType && d.ServiceKey == descriptor.ServiceKey))
{
return;
}
services.Add(descriptor);
- _ = services.Activate();
+ _ = services.ActivateSingleton();
}
private static void TryAddAndActivate(this IServiceCollection services, ServiceDescriptor descriptor)
{
- if (services.Any(d => d.ServiceType == descriptor.ServiceType))
+ if (services.Any(d => d.ServiceType == descriptor.ServiceType && d.ServiceKey == descriptor.ServiceKey))
{
return;
}
services.Add(descriptor);
- _ = services.Activate(descriptor.ServiceType);
+ _ = services.ActivateSingleton(descriptor.ServiceType);
}
}
diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationHostedService.cs b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationHostedService.cs
index b33c748e44c..7f69d63fd18 100644
--- a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationHostedService.cs
+++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivationHostedService.cs
@@ -2,10 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Diagnostics;
@@ -14,24 +12,27 @@ namespace Microsoft.Extensions.DependencyInjection;
internal sealed class AutoActivationHostedService : IHostedService
{
- private readonly Type[] _autoActivators;
+ private readonly AutoActivatorOptions _options;
private readonly IServiceProvider _provider;
public AutoActivationHostedService(IServiceProvider provider, IOptions options)
{
_provider = provider;
- var value = Throw.IfMemberNull(options, options.Value);
-
- _autoActivators = value.AutoActivators.ToArray();
+ _options = Throw.IfMemberNull(options, options.Value);
}
public Task StartAsync(CancellationToken cancellationToken)
{
- foreach (var singleton in _autoActivators)
+ foreach (var singleton in _options.AutoActivators)
{
_ = _provider.GetRequiredService(singleton);
}
+ foreach (var (serviceType, serviceKey) in _options.KeyedAutoActivators)
+ {
+ _ = _provider.GetRequiredKeyedService(serviceType, serviceKey);
+ }
+
return Task.CompletedTask;
}
diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs
index d1a2ed07df3..c2aa14f3177 100644
--- a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs
+++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/AutoActivatorOptions.cs
@@ -9,4 +9,5 @@ namespace Microsoft.Extensions.DependencyInjection;
internal sealed class AutoActivatorOptions
{
public HashSet AutoActivators { get; } = new();
+ public HashSet<(Type serviceType, object? serviceKey)> KeyedAutoActivators { get; } = new();
}
diff --git a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/Microsoft.Extensions.DependencyInjection.AutoActivation.csproj b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/Microsoft.Extensions.DependencyInjection.AutoActivation.csproj
index 9b17c21e82b..94805a22c97 100644
--- a/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/Microsoft.Extensions.DependencyInjection.AutoActivation.csproj
+++ b/src/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation/Microsoft.Extensions.DependencyInjection.AutoActivation.csproj
@@ -5,6 +5,10 @@
Fundamentals
+
+ true
+
+
normal
100
diff --git a/src/Shared/DiagnosticIds/Experiments.cs b/src/Shared/DiagnosticIds/Experiments.cs
index db9f038280d..dd7cbeed828 100644
--- a/src/Shared/DiagnosticIds/Experiments.cs
+++ b/src/Shared/DiagnosticIds/Experiments.cs
@@ -10,7 +10,7 @@ namespace Microsoft.Shared.DiagnosticIds;
///
///
/// When adding a new experiment, add a corresponding suppression to the root Directory.Build.targets file, and add a documentation entry to
-/// docs/Experiments.md.
+/// docs/list-of-diagnostics.md.
///
internal static class Experiments
{
@@ -29,4 +29,5 @@ internal static class Experiments
internal const string Hosting = "EXTEXP0009";
internal const string ObjectPool = "EXTEXP0010";
internal const string DocumentDb = "EXTEXP0011";
+ internal const string AutoActivation = "EXTEXP0012";
}
diff --git a/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.Keyed.cs b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.Keyed.cs
new file mode 100644
index 00000000000..ec45d077598
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.Keyed.cs
@@ -0,0 +1,426 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection.Test.Fakes;
+using Microsoft.Extensions.DependencyInjection.Test.Helpers;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Hosting.Testing;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection.Test;
+
+public partial class AcceptanceTest
+{
+ [Fact]
+ public async Task CanAddAndActivateKeyedSingletonAsync()
+ {
+ var instanceCount = new InstanceCreatingCounter();
+ Assert.Equal(0, instanceCount.Counter);
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(instanceCount)
+ .AddActivatedKeyedSingleton(serviceKey))
+ .StartAsync();
+
+ Assert.Equal(1, instanceCount.Counter);
+
+ var service = host.Services.GetKeyedService(serviceKey);
+ await host.StopAsync();
+
+ Assert.NotNull(service);
+ Assert.Equal(1, instanceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldAddAndActivateOnlyOnce_WhenHasChildAsync_Keyed()
+ {
+ var parentCount = new InstanceCreatingCounter();
+ var childCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services.AddSingleton(childCount)
+ .AddSingleton(parentCount)
+ .AddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(FakeService))
+ .AddActivatedKeyedSingleton(serviceKey, (sp, sk) =>
+ {
+ return new FactoryService(sp.GetKeyedService(sk)!, sp.GetRequiredService());
+ }))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, childCount.Counter);
+ Assert.Equal(1, parentCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldResolveComponentsAutomaticallyAsync_Keyed()
+ {
+ var parentCount = new InstanceCreatingCounter();
+ var childCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(childCount)
+ .AddSingleton(parentCount)
+ .AddSingleton()
+ .AddActivatedKeyedSingleton(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, childCount.Counter);
+ Assert.Equal(1, parentCount.Counter);
+ }
+
+ [Fact]
+ public async Task CanActivateEnumerableAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeFactoryCount = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeFactoryCount)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(FakeService))
+ .AddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(FakeOneMultipleService))
+ .AddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(AnotherFakeService)))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(1, fakeFactoryCount.Counter);
+ Assert.Equal(1, anotherFakeServiceCount.Counter);
+
+ await host.StopAsync();
+ }
+
+ [Fact]
+ public async Task CanActivateEnumerableAsync_WithTypeArg_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeFactoryCount = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeFactoryCount)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddActivatedKeyedSingleton(serviceKey)
+ .AddActivatedKeyedSingleton(serviceKey)
+ .AddActivatedKeyedSingleton(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(1, fakeFactoryCount.Counter);
+ Assert.Equal(1, anotherFakeServiceCount.Counter);
+
+ await host.StopAsync();
+ }
+
+ [Fact]
+ public async Task CanActivateOneServiceAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddSingleton()
+ .AddActivatedKeyedSingleton(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(0, fakeServiceCount.Counter);
+ Assert.Equal(1, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldActivateService_WhenTypeIsSpecifiedInTypeParameterTService_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddActivatedKeyedSingleton(serviceKey)
+ .AddActivatedKeyedSingleton(serviceKey, (sp, _) => new AnotherFakeService(sp.GetRequiredService())))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, counter.Counter);
+ Assert.Equal(1, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldActivateService_WhenTypeIsSpecifiedInParameter_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddActivatedKeyedSingleton(typeof(FakeService), serviceKey)
+ .AddActivatedKeyedSingleton(typeof(AnotherFakeService), serviceKey, (sp, _) => new AnotherFakeService(sp.GetService()!)))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, counter.Counter);
+ Assert.Equal(1, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task TestStopHostAsync_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(counter)
+ .AddActivatedKeyedSingleton(serviceKey))
+ .StartAsync();
+
+ Assert.Equal(1, counter.Counter);
+ await host.StopAsync();
+ }
+
+ [Fact]
+ public async Task ShouldNotActivate_WhenServiceOfTypeSpecifiedInTypeParameter_WasAlreadyAdded_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddKeyedSingleton(serviceKey)
+ .AddKeyedSingleton(serviceKey);
+ services.TryAddActivatedKeyedSingleton(typeof(FakeService), serviceKey);
+ services.TryAddActivatedKeyedSingleton(typeof(AnotherFakeService), serviceKey, (sp, _) => new AnotherFakeService(sp.GetService()!));
+ })
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(0, counter.Counter);
+ Assert.Equal(0, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldNotActivate_WhenServiceOfTypeSpecifiedInParameter_WasAlreadyAdded_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount)
+ .AddKeyedSingleton(serviceKey)
+ .AddKeyedSingleton(serviceKey);
+ services.TryAddActivatedKeyedSingleton(serviceKey);
+ services.TryAddActivatedKeyedSingleton(serviceKey, (sp, _) => new AnotherFakeService(sp.GetService()!));
+ })
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(0, counter.Counter);
+ Assert.Equal(0, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldActivateOneSingleton_WhenTryAddIsCalled_WithTypeSpecifiedImplementation_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount);
+ services.TryAddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(FakeService));
+ services.TryAddActivatedKeyedSingleton(typeof(IFakeService), serviceKey, typeof(AnotherFakeService));
+ })
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, counter.Counter);
+ Assert.Equal(0, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ShouldActivateOneSingleton_WhenTryAddIsCalled_WithTypeSpecifiedImplementation_WithTypeArg_Keyed()
+ {
+ var counter = new InstanceCreatingCounter();
+ var anotherFakeServiceCount = new AnotherFakeServiceCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services =>
+ {
+ services
+ .AddSingleton(counter)
+ .AddSingleton(anotherFakeServiceCount);
+ services.TryAddActivatedKeyedSingleton(serviceKey);
+ services.TryAddActivatedKeyedSingleton(serviceKey);
+ })
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, counter.Counter);
+ Assert.Equal(0, anotherFakeServiceCount.Counter);
+ }
+
+ [Fact]
+ public async Task CanActivateSingletonAsync_Keyed()
+ {
+ var instanceCount = new InstanceCreatingCounter();
+ Assert.Equal(0, instanceCount.Counter);
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(instanceCount)
+ .AddKeyedSingleton(serviceKey)
+ .ActivateKeyedSingleton(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, instanceCount.Counter);
+
+ var service = host.Services.GetKeyedService(serviceKey);
+
+ Assert.NotNull(service);
+ Assert.Equal(1, instanceCount.Counter);
+ }
+
+ [Fact]
+ public async Task ActivationOfNotRegisteredType_ThrowsExceptionAsync_Keyed()
+ {
+ var serviceKey = new object();
+ using var host = FakeHost.CreateBuilder()
+ .ConfigureServices(services => services.ActivateKeyedSingleton(serviceKey))
+ .Build();
+
+ var exception = await Assert.ThrowsAsync(() => host.StartAsync());
+
+ Assert.Contains(typeof(IFakeService).FullName!, exception.Message);
+ }
+
+ [Fact]
+ public async Task CanActivateEnumerableImplicitlyAddedAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeFactoryCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeFactoryCount)
+ .AddKeyedSingleton(serviceKey).ActivateKeyedSingleton(serviceKey)
+ .AddKeyedSingleton(serviceKey).ActivateKeyedSingleton(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(1, fakeFactoryCount.Counter);
+ }
+
+ [Fact]
+ public async Task CanActivateEnumerableExplicitlyAddedAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeFactoryCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeFactoryCount)
+ .AddKeyedSingleton(serviceKey)
+ .AddKeyedSingleton(serviceKey)
+ .ActivateKeyedSingleton>(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(1, fakeFactoryCount.Counter);
+ }
+
+ [Fact]
+ public async Task CanAutoActivateOpenGenericsAsEnumerableAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeOpenGenericCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await new HostBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeOpenGenericCount)
+ .AddTransient()
+ .AddKeyedSingleton(typeof(IFakeOpenGenericService), serviceKey, typeof(FakeService))
+ .AddKeyedSingleton(typeof(IFakeOpenGenericService<>), serviceKey, typeof(FakeOpenGenericService<>))
+ .ActivateKeyedSingleton>>(serviceKey)
+ .ActivateKeyedSingleton>(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(2, fakeOpenGenericCount.Counter);
+ }
+
+ [Fact]
+ public async Task CanAutoActivateClosedGenericsAsEnumerableAsync_Keyed()
+ {
+ var fakeServiceCount = new InstanceCreatingCounter();
+ var fakeOpenGenericCount = new InstanceCreatingCounter();
+
+ var serviceKey = new object();
+ using var host = await FakeHost.CreateBuilder()
+ .ConfigureServices(services => services
+ .AddSingleton(fakeServiceCount)
+ .AddSingleton(fakeOpenGenericCount)
+ .AddTransient()
+ .AddKeyedSingleton(typeof(IFakeOpenGenericService), serviceKey, typeof(FakeService))
+ .AddKeyedSingleton, FakeOpenGenericService>(serviceKey)
+ .ActivateKeyedSingleton>>(serviceKey))
+ .StartAsync();
+ await host.StopAsync();
+
+ Assert.Equal(1, fakeServiceCount.Counter);
+ Assert.Equal(1, fakeOpenGenericCount.Counter);
+ }
+}
diff --git a/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.cs b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.cs
index 622e9c3957a..b5deaa71725 100644
--- a/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.cs
+++ b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AcceptanceTest.cs
@@ -12,7 +12,7 @@
namespace Microsoft.Extensions.DependencyInjection.Test;
-public class AcceptanceTest
+public partial class AcceptanceTest
{
[Fact]
public async Task CanAddAndActivateSingletonAsync()
@@ -26,6 +26,8 @@ public async Task CanAddAndActivateSingletonAsync()
.AddActivatedSingleton())
.StartAsync();
+ Assert.Equal(1, instanceCount.Counter);
+
var service = host.Services.GetService();
await host.StopAsync();
@@ -34,7 +36,7 @@ public async Task CanAddAndActivateSingletonAsync()
}
[Fact]
- public async Task SouldIgnoreComponent_WhenNoAutoStartAsync()
+ public async Task ShouldIgnoreComponent_WhenNoAutoStartAsync()
{
var instanceCount = new InstanceCreatingCounter();
Assert.Equal(0, instanceCount.Counter);
@@ -64,9 +66,9 @@ public async Task ShouldAddAndActivateOnlyOnce_WhenHasChildAsync()
.ConfigureServices(services => services.AddSingleton(childCount)
.AddSingleton(parentCount)
.AddActivatedSingleton(typeof(IFakeService), typeof(FakeService))
- .AddActivatedSingleton(_ =>
+ .AddActivatedSingleton(sp =>
{
- return new FactoryService(_.GetService()!, _.GetService()!);
+ return new FactoryService(sp.GetService()!, sp.GetService()!);
}))
.StartAsync();
await host.StopAsync();
@@ -174,7 +176,7 @@ public async Task ShouldActivateService_WhenTypeIsSpecifiedInTypeParameterTServi
.AddSingleton(counter)
.AddSingleton(anotherFakeServiceCount)
.AddActivatedSingleton()
- .AddActivatedSingleton(_ => new AnotherFakeService(_.GetService()!)))
+ .AddActivatedSingleton(sp => new AnotherFakeService(sp.GetService()!)))
.StartAsync();
await host.StopAsync();
@@ -193,7 +195,7 @@ public async Task ShouldActivateService_WhenTypeIsSpecifiedInParameter()
.AddSingleton(counter)
.AddSingleton(anotherFakeServiceCount)
.AddActivatedSingleton(typeof(FakeService))
- .AddActivatedSingleton(typeof(AnotherFakeService), _ => new AnotherFakeService(_.GetService()!)))
+ .AddActivatedSingleton(typeof(AnotherFakeService), sp => new AnotherFakeService(sp.GetService()!)))
.StartAsync();
await host.StopAsync();
@@ -231,7 +233,7 @@ public async Task ShouldNotActivate_WhenServiceOfTypeSpecifiedInTypeParameter_Wa
.AddSingleton()
.AddSingleton();
services.TryAddActivatedSingleton(typeof(FakeService));
- services.TryAddActivatedSingleton(typeof(AnotherFakeService), _ => new AnotherFakeService(_.GetService()!));
+ services.TryAddActivatedSingleton(typeof(AnotherFakeService), sp => new AnotherFakeService(sp.GetService()!));
})
.StartAsync();
await host.StopAsync();
@@ -255,7 +257,7 @@ public async Task ShouldNotActivate_WhenServiceOfTypeSpecifiedInParameter_WasAlr
.AddSingleton()
.AddSingleton();
services.TryAddActivatedSingleton();
- services.TryAddActivatedSingleton(_ => new AnotherFakeService(_.GetService()!));
+ services.TryAddActivatedSingleton(sp => new AnotherFakeService(sp.GetService()!));
})
.StartAsync();
await host.StopAsync();
@@ -308,7 +310,6 @@ public async Task ShouldActivateOneSingleton_WhenTryAddIsCalled_WithTypeSpecifie
Assert.Equal(0, anotherFakeServiceCount.Counter);
}
- // ------------------------------------------------------------------------------
[Fact]
public async Task CanActivateSingletonAsync()
{
@@ -319,7 +320,7 @@ public async Task CanActivateSingletonAsync()
.ConfigureServices(services => services
.AddSingleton(instanceCount)
.AddSingleton()
- .Activate())
+ .ActivateSingleton())
.StartAsync();
await host.StopAsync();
@@ -335,7 +336,7 @@ public async Task CanActivateSingletonAsync()
public async Task ActivationOfNotRegisteredType_ThrowsExceptionAsync()
{
using var host = FakeHost.CreateBuilder()
- .ConfigureServices(services => services.Activate())
+ .ConfigureServices(services => services.ActivateSingleton())
.Build();
var exception = await Assert.ThrowsAsync(() => host.StartAsync());
@@ -353,8 +354,8 @@ public async Task CanActivateEnumerableImplicitlyAddedAsync()
.ConfigureServices(services => services
.AddSingleton(fakeServiceCount)
.AddSingleton(fakeFactoryCount)
- .AddSingleton().Activate(typeof(IFakeService))
- .AddSingleton().Activate(typeof(IFakeService)))
+ .AddSingleton().ActivateSingleton(typeof(IFakeService))
+ .AddSingleton().ActivateSingleton(typeof(IFakeService)))
.StartAsync();
await host.StopAsync();
@@ -372,8 +373,8 @@ public async Task CanActivateEnumerableImplicitlyAddedAsync_WithTypeArg()
.ConfigureServices(services => services
.AddSingleton(fakeServiceCount)
.AddSingleton(fakeFactoryCount)
- .AddSingleton().Activate()
- .AddSingleton().Activate())
+ .AddSingleton().ActivateSingleton()
+ .AddSingleton().ActivateSingleton())
.StartAsync();
await host.StopAsync();
@@ -393,7 +394,7 @@ public async Task CanActivateEnumerableExplicitlyAddedAsync()
.AddSingleton(fakeFactoryCount)
.AddSingleton()
.AddSingleton()
- .Activate>())
+ .ActivateSingleton>())
.StartAsync();
await host.StopAsync();
@@ -414,8 +415,8 @@ public async Task CanAutoActivateOpenGenericsAsEnumerableAsync()
.AddTransient()
.AddSingleton(typeof(IFakeOpenGenericService), typeof(FakeService))
.AddSingleton(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>))
- .Activate>>()
- .Activate>())
+ .ActivateSingleton>>()
+ .ActivateSingleton>())
.StartAsync();
await host.StopAsync();
@@ -436,7 +437,7 @@ public async Task CanAutoActivateClosedGenericsAsEnumerableAsync()
.AddTransient()
.AddSingleton(typeof(IFakeOpenGenericService), typeof(FakeService))
.AddSingleton, FakeOpenGenericService>()
- .Activate>>())
+ .ActivateSingleton>>())
.StartAsync();
await host.StopAsync();
diff --git a/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AutoActivationExtensionsKeyedTests.cs b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AutoActivationExtensionsKeyedTests.cs
new file mode 100644
index 00000000000..a01af835609
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.DependencyInjection.AutoActivation.Tests/AutoActivationExtensionsKeyedTests.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Test.Fakes;
+using Microsoft.Extensions.DependencyInjection.Test.Helpers;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection.Test;
+
+public class AutoActivationExtensionsKeyedTests
+{
+ [Fact]
+ public void AddActivatedKeyedSingleton_Throws_WhenArgumentsAreNull()
+ {
+ var serviceCollection = new ServiceCollection();
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, null, (_, _) => null!));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, null, null!));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, null, (_, _) => null!));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, null, null!));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, null));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, typeof(FakeService), null));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, null!, null));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, null));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, typeof(FakeService), null, (_, _) => null!));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, null!, null, (_, _) => null!));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, typeof(FakeService), null, implementationFactory: null!));
+
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(null!, typeof(IFakeService), null, typeof(FakeService)));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, null!, null, typeof(FakeService)));
+ Assert.Throws(() => AutoActivationExtensions.AddActivatedKeyedSingleton(serviceCollection, typeof(IFakeService), null, implementationType: null!));
+ }
+
+ [Fact]
+ public void TryAddActivatedKeyedSingleton_Throws_WhenArgumentsAreNull()
+ {
+ var serviceCollection = new ServiceCollection();
+ Assert.Throws(() => AutoActivationExtensions.TryAddActivatedKeyedSingleton(null!, typeof(FakeService), null));
+ Assert.Throws(() => AutoActivationExtensions.TryAddActivatedKeyedSingleton(serviceCollection, null!, null));
+
+ Assert.Throws(() => AutoActivationExtensions.TryAddActivatedKeyedSingleton(null!, typeof(IFakeService), null, typeof(FakeService)));
+ Assert.Throws(() => AutoActivationExtensions.TryAddActivatedKeyedSingleton(serviceCollection, null!, null, typeof(FakeService)));
+ Assert.Throws