public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
}
+ [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)]
+ public partial class FromKeyedServicesAttribute : System.Attribute
+ {
+ public FromKeyedServicesAttribute(object key) { }
+ public object Key { get { throw null; } }
+ }
public partial interface IServiceCollection : System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IEnumerable<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IList<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.IEnumerable
{
}
{
bool IsService(System.Type serviceType);
}
+ public partial interface IServiceProviderIsKeyedService : IServiceProviderIsService
+ {
+ bool IsKeyedService(System.Type serviceType, object? serviceKey);
+ }
public partial interface IServiceScope : System.IDisposable
{
System.IServiceProvider ServiceProvider { get; }
{
Microsoft.Extensions.DependencyInjection.IServiceScope CreateScope();
}
+ public partial interface IKeyedServiceProvider : System.IServiceProvider
+ {
+ object? GetKeyedService(System.Type serviceType, object? serviceKey);
+ object GetRequiredKeyedService(System.Type serviceType, object? serviceKey);
+ }
public partial interface ISupportRequiredService
{
object GetRequiredService(System.Type serviceType);
}
+ public static partial class KeyedService
+ {
+ public static object AnyKey { get { throw null; } }
+ }
public delegate object ObjectFactory(System.IServiceProvider serviceProvider, object?[]? arguments);
public delegate T ObjectFactory<T>(System.IServiceProvider serviceProvider, object?[]? arguments);
public partial class ServiceCollection : Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IEnumerable<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IList<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.IEnumerable
}
public static partial class ServiceCollectionServiceExtensions
{
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type serviceType, object? serviceKey) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedScoped<TService, TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type serviceType, object? serviceKey) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, object implementationInstance) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, TService implementationInstance) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedSingleton<TService, TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type serviceType, object? serviceKey) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddKeyedTransient<TService, TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type serviceType) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, System.Func<System.IServiceProvider, object> implementationFactory) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Type serviceType, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
{
public ServiceDescriptor(System.Type serviceType, System.Func<System.IServiceProvider, object> factory, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { }
public ServiceDescriptor(System.Type serviceType, object instance) { }
+ public ServiceDescriptor(System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object?, object> factory, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { }
+ public ServiceDescriptor(System.Type serviceType, object? serviceKey, object instance) { }
+ public ServiceDescriptor(System.Type serviceType, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { }
public ServiceDescriptor(System.Type serviceType, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { }
public System.Func<System.IServiceProvider, object>? ImplementationFactory { get { throw null; } }
public object? ImplementationInstance { get { throw null; } }
[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
public System.Type? ImplementationType { get { throw null; } }
+ public bool IsKeyedService { get { throw null; } }
+ public System.Func<System.IServiceProvider, object?, object>? KeyedImplementationFactory { get { throw null; } }
+ public object? KeyedImplementationInstance { get { throw null; } }
+ [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)]
+ public System.Type? KeyedImplementationType { get { throw null; } }
public Microsoft.Extensions.DependencyInjection.ServiceLifetime Lifetime { get { throw null; } }
+ public object? ServiceKey { get { throw null; } }
public System.Type ServiceType { get { throw null; } }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Describe(System.Type serviceType, System.Func<System.IServiceProvider, object> implementationFactory, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Describe(System.Type serviceType, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor DescribeKeyed(System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor DescribeKeyed(System.Type serviceType, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType, Microsoft.Extensions.DependencyInjection.ServiceLifetime lifetime) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedScoped(System.Type service, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedScoped(System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedScoped<TService>(object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedScoped<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedScoped<TService, TImplementation>(object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton(System.Type serviceType, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton(System.Type serviceType, object? serviceKey, object implementationInstance) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton(System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton<TService>(object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton<TService>(object? serviceKey, TService implementationInstance) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedSingleton<TService, TImplementation>(object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedTransient(System.Type service, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedTransient(System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedTransient<TService>(object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedTransient<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey) where TService : class where TImplementation : class, TService { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor KeyedTransient<TService, TImplementation>(object? serviceKey, System.Func<System.IServiceProvider, object, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Scoped(System.Type service, System.Func<System.IServiceProvider, object> implementationFactory) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Scoped(System.Type service, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Scoped<TService>(System.Func<System.IServiceProvider, TService> implementationFactory) where TService : class { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Transient<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>() where TService : class where TImplementation : class, TService { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceDescriptor Transient<TService, TImplementation>(System.Func<System.IServiceProvider, TImplementation> implementationFactory) where TService : class where TImplementation : class, TService { throw null; }
}
+ [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)]
+ public partial class ServiceKeyAttribute : System.Attribute
+ {
+ public ServiceKeyAttribute() { }
+ }
public enum ServiceLifetime
{
Singleton = 0,
Scoped = 1,
Transient = 2,
}
+ public static partial class ServiceProviderKeyedServiceExtensions
+ {
+ [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("The native code for an IEnumerable<serviceType> might not be available at runtime.")]
+ public static System.Collections.Generic.IEnumerable<object?> GetKeyedServices(this System.IServiceProvider provider, System.Type serviceType, object? serviceKey) { throw null; }
+ public static System.Collections.Generic.IEnumerable<T> GetKeyedServices<T>(this System.IServiceProvider provider, object? serviceKey) { throw null; }
+ public static T? GetKeyedService<T>(this System.IServiceProvider provider, object? serviceKey) { throw null; }
+ public static object GetRequiredKeyedService(this System.IServiceProvider provider, System.Type serviceType, object? serviceKey) { throw null; }
+ public static T GetRequiredKeyedService<T>(this System.IServiceProvider provider, object? serviceKey) where T : notnull { throw null; }
+ }
public static partial class ServiceProviderServiceExtensions
{
public static Microsoft.Extensions.DependencyInjection.AsyncServiceScope CreateAsyncScope(this Microsoft.Extensions.DependencyInjection.IServiceScopeFactory serviceScopeFactory) { throw null; }
public static void TryAddTransient<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection) where TService : class { }
public static void TryAddTransient<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Func<System.IServiceProvider, TService> implementationFactory) where TService : class { }
public static void TryAddTransient<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection) where TService : class where TImplementation : class, TService { }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection RemoveAllKeyed(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type serviceType, object? serviceKey) { throw null; }
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection RemoveAllKeyed<T>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) { throw null; }
+ public static void TryAddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type service, object? serviceKey) { }
+ public static void TryAddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { }
+ public static void TryAddKeyedScoped(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { }
+ public static void TryAddKeyedScoped<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class { }
+ public static void TryAddKeyedScoped<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { }
+ public static void TryAddKeyedScoped<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class where TImplementation : class, TService { }
+ public static void TryAddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type service, object? serviceKey) { }
+ public static void TryAddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { }
+ public static void TryAddKeyedSingleton(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { }
+ public static void TryAddKeyedSingleton<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class { }
+ public static void TryAddKeyedSingleton<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { }
+ public static void TryAddKeyedSingleton<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey, TService instance) where TService : class { }
+ public static void TryAddKeyedSingleton<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class where TImplementation : class, TService { }
+ public static void TryAddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type service, object? serviceKey) { }
+ public static void TryAddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, System.Func<System.IServiceProvider, object, object> implementationFactory) { }
+ public static void TryAddKeyedTransient(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, System.Type service, object? serviceKey, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type implementationType) { }
+ public static void TryAddKeyedTransient<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class { }
+ public static void TryAddKeyedTransient<TService>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, object? serviceKey, System.Func<System.IServiceProvider, object, TService> implementationFactory) where TService : class { }
+ public static void TryAddKeyedTransient<TService, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this Microsoft.Extensions.DependencyInjection.IServiceCollection collection, object? serviceKey) where TService : class where TImplementation : class, TService { }
}
}
return true;
}
+ private static object? GetService(IServiceProvider serviceProvider, ParameterInfo parameterInfo)
+ {
+ // Handle keyed service
+ if (TryGetServiceKey(parameterInfo, out object? key))
+ {
+ if (serviceProvider is IKeyedServiceProvider keyedServiceProvider)
+ {
+ return keyedServiceProvider.GetKeyedService(parameterInfo.ParameterType, key);
+ }
+ throw new InvalidOperationException(SR.KeyedServicesNotSupported);
+ }
+ // Try non keyed service
+ return serviceProvider.GetService(parameterInfo.ParameterType);
+ }
+
+ private static bool IsService(IServiceProviderIsService serviceProviderIsService, ParameterInfo parameterInfo)
+ {
+ // Handle keyed service
+ if (TryGetServiceKey(parameterInfo, out object? key))
+ {
+ if (serviceProviderIsService is IServiceProviderIsKeyedService serviceProviderIsKeyedService)
+ {
+ return serviceProviderIsKeyedService.IsKeyedService(parameterInfo.ParameterType, key);
+ }
+ throw new InvalidOperationException(SR.KeyedServicesNotSupported);
+ }
+ // Try non keyed service
+ return serviceProviderIsService.IsService(parameterInfo.ParameterType);
+ }
+
+ private static bool TryGetServiceKey(ParameterInfo parameterInfo, out object? key)
+ {
+ if (parameterInfo.CustomAttributes != null)
+ {
+ foreach (var attribute in parameterInfo.GetCustomAttributes(true))
+ {
+ if (attribute is FromKeyedServicesAttribute keyed)
+ {
+ key = keyed.Key;
+ return true;
+ }
+ }
+ }
+ key = null;
+ return false;
+ }
+
private readonly struct ConstructorMatcher
{
private readonly ConstructorInfo _constructor;
for (int i = 0; i < _parameters.Length; i++)
{
if (_parameterValues[i] == null &&
- !serviceProviderIsService.IsService(_parameters[i].ParameterType))
+ !IsService(serviceProviderIsService, _parameters[i]))
{
if (ParameterDefaultValue.TryGetDefaultValue(_parameters[i], out object? defaultValue))
{
{
if (_parameterValues[index] == null)
{
- object? value = provider.GetService(_parameters[index].ParameterType);
+ object? value = GetService(provider, _parameters[index]);
if (value == null)
{
if (!ParameterDefaultValue.TryGetDefaultValue(_parameters[index], out object? defaultValue))
--- /dev/null
+// 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;
+
+namespace Microsoft.Extensions.DependencyInjection.Extensions
+{
+ public static partial class ServiceCollectionDescriptorExtensions
+ {
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedTransient(
+ this IServiceCollection collection,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type service,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+
+ var descriptor = ServiceDescriptor.KeyedTransient(service, serviceKey, service);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// with the <paramref name="implementationType"/> implementation
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ public static void TryAddKeyedTransient(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ var descriptor = ServiceDescriptor.KeyedTransient(service, serviceKey, implementationType);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ public static void TryAddKeyedTransient(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ var descriptor = ServiceDescriptor.KeyedTransient(service, serviceKey, implementationFactory);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedTransient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedTransient(collection, typeof(TService), serviceKey, typeof(TService));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// implementation type specified in <typeparamref name="TImplementation"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedTransient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedTransient(collection, typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Transient"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="services"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ public static void TryAddKeyedTransient<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ services.TryAdd(ServiceDescriptor.KeyedTransient(serviceKey, implementationFactory));
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedScoped(
+ this IServiceCollection collection,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type service,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+
+ var descriptor = ServiceDescriptor.KeyedScoped(service, serviceKey, service);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// with the <paramref name="implementationType"/> implementation
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ public static void TryAddKeyedScoped(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ var descriptor = ServiceDescriptor.KeyedScoped(service, serviceKey, implementationType);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ public static void TryAddKeyedScoped(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ var descriptor = ServiceDescriptor.KeyedScoped(service, serviceKey, implementationFactory);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedScoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedScoped(collection, typeof(TService), serviceKey, typeof(TService));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// implementation type specified in <typeparamref name="TImplementation"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedScoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedScoped(collection, typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Scoped"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="services"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedScoped<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ services.TryAdd(ServiceDescriptor.KeyedScoped(serviceKey, implementationFactory));
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedSingleton(
+ this IServiceCollection collection,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type service,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+
+ var descriptor = ServiceDescriptor.KeyedSingleton(service, serviceKey, service);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// with the <paramref name="implementationType"/> implementation
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ public static void TryAddKeyedSingleton(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ var descriptor = ServiceDescriptor.KeyedSingleton(service, serviceKey, implementationType);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <paramref name="service"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="service">The type of the service to register.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ public static void TryAddKeyedSingleton(
+ this IServiceCollection collection,
+ Type service,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ var descriptor = ServiceDescriptor.KeyedSingleton(service, serviceKey, implementationFactory);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedSingleton(collection, typeof(TService), serviceKey, typeof(TService));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// implementation type specified in <typeparamref name="TImplementation"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ public static void TryAddKeyedSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection collection, object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(collection);
+
+ TryAddKeyedSingleton(collection, typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// with an instance specified in <paramref name="instance"/>
+ /// to the <paramref name="collection"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="instance">The instance of the service to add.</param>
+ public static void TryAddKeyedSingleton<TService>(this IServiceCollection collection, object? serviceKey, TService instance)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(collection);
+ ThrowHelper.ThrowIfNull(instance);
+
+ var descriptor = ServiceDescriptor.KeyedSingleton(serviceType: typeof(TService), serviceKey, implementationInstance: instance);
+ ServiceCollectionDescriptorExtensions.TryAdd(collection, descriptor);
+ }
+
+ /// <summary>
+ /// Adds the specified <typeparamref name="TService"/> as a <see cref="ServiceLifetime.Singleton"/> service
+ /// using the factory specified in <paramref name="implementationFactory"/>
+ /// to the <paramref name="services"/> if the service type hasn't already been registered.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ public static void TryAddKeyedSingleton<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ services.TryAdd(ServiceDescriptor.KeyedSingleton(serviceKey, implementationFactory));
+ }
+
+ /// <summary>
+ /// Removes all services of type <typeparamref name="T"/> in <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
+ public static IServiceCollection RemoveAllKeyed<T>(this IServiceCollection collection, object? serviceKey)
+ {
+ return RemoveAllKeyed(collection, typeof(T), serviceKey);
+ }
+
+ /// <summary>
+ /// Removes all services of type <paramref name="serviceType"/> in <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="collection">The <see cref="IServiceCollection"/>.</param>
+ /// <param name="serviceType">The service type to remove.</param>
+ /// <param name="serviceKey">The service key.</param>
+ /// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
+ public static IServiceCollection RemoveAllKeyed(this IServiceCollection collection, Type serviceType, object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ for (int i = collection.Count - 1; i >= 0; i--)
+ {
+ ServiceDescriptor? descriptor = collection[i];
+ if (descriptor.ServiceType == serviceType && descriptor.ServiceKey == serviceKey)
+ {
+ collection.RemoveAt(i);
+ }
+ }
+
+ return collection;
+ }
+ }
+}
/// <summary>
/// Extension methods for adding and removing services to an <see cref="IServiceCollection" />.
/// </summary>
- public static class ServiceCollectionDescriptorExtensions
+ public static partial class ServiceCollectionDescriptorExtensions
{
/// <summary>
/// Adds the specified <paramref name="descriptor"/> to the <paramref name="collection"/>.
int count = collection.Count;
for (int i = 0; i < count; i++)
{
- if (collection[i].ServiceType == descriptor.ServiceType)
+ if (collection[i].ServiceType == descriptor.ServiceType
+ && collection[i].ServiceKey == descriptor.ServiceKey)
{
// Already added
return;
ThrowHelper.ThrowIfNull(collection);
ThrowHelper.ThrowIfNull(instance);
- var descriptor = ServiceDescriptor.Singleton(typeof(TService), instance);
+ var descriptor = ServiceDescriptor.Singleton(serviceType: typeof(TService), implementationInstance: instance);
TryAdd(collection, descriptor);
}
{
ServiceDescriptor service = services[i];
if (service.ServiceType == descriptor.ServiceType &&
- service.GetImplementationType() == implementationType)
+ service.GetImplementationType() == implementationType &&
+ service.ServiceKey == descriptor.ServiceKey)
{
// Already added
return;
int count = collection.Count;
for (int i = 0; i < count; i++)
{
- if (collection[i].ServiceType == descriptor.ServiceType)
+ if (collection[i].ServiceType == descriptor.ServiceType && collection[i].ServiceKey == descriptor.ServiceKey)
{
collection.RemoveAt(i);
break;
for (int i = collection.Count - 1; i >= 0; i--)
{
ServiceDescriptor? descriptor = collection[i];
- if (descriptor.ServiceType == serviceType)
+ if (descriptor.ServiceType == serviceType && descriptor.ServiceKey == null)
{
collection.RemoveAt(i);
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class FromKeyedServicesAttribute : Attribute
+ {
+ public FromKeyedServicesAttribute(object key) => Key = key;
+
+ public object Key { get; }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public interface IKeyedServiceProvider : IServiceProvider
+ {
+ /// <summary>
+ /// Gets the service object of the specified type.
+ /// </summary>
+ /// <param name="serviceType">An object that specifies the type of service object to get.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns> A service object of type serviceType. -or- null if there is no service object of type serviceType.</returns>
+ object? GetKeyedService(Type serviceType, object? serviceKey);
+
+ /// <summary>
+ /// Gets service of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/> implementing
+ /// this interface.
+ /// </summary>
+ /// <param name="serviceType">An object that specifies the type of service object to get.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A service object of type <paramref name="serviceType"/>.
+ /// Throws an exception if the <see cref="IServiceProvider"/> cannot create the object.</returns>
+ object GetRequiredKeyedService(Type serviceType, object? serviceKey);
+ }
+
+ public static class KeyedService
+ {
+ public static object AnyKey { get; } = new AnyKeyObj();
+
+ private sealed class AnyKeyObj
+ {
+ public override string? ToString() => "*";
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public interface IServiceProviderIsKeyedService : IServiceProviderIsService
+ {
+ /// <summary>
+ /// Determines if the specified service type is available from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <param name="serviceType">An object that specifies the type of service object to test.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>true if the specified service is a available, false if it is not.</returns>
+ bool IsKeyedService(Type serviceType, object? serviceKey);
+ }
+}
<value>Multiple constructors accepting all given argument types have been found in type '{0}'. There should only be one applicable constructor.</value>
<comment>{0} = instance type</comment>
</data>
+ <data name="KeyedServicesNotSupported" xml:space="preserve">
+ <value>This service provider doesn't support keyed services.</value>
+ </data>
+ <data name="KeyedDescriptorMisuse" xml:space="preserve">
+ <value>This service descriptor is keyed. Your service provider may not support keyed services.</value>
+ </data>
+ <data name="NonKeyedDescriptorMisuse" xml:space="preserve">
+ <value>This service descriptor is not keyed.</value>
+ </data>
</root>
\ No newline at end of file
--- /dev/null
+// 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.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ /// <summary>
+ /// Extension methods for adding services to an <see cref="IServiceCollection" />.
+ /// </summary>
+ public static partial class ServiceCollectionServiceExtensions
+ {
+ /// <summary>
+ /// Adds a transient service of the type specified in <paramref name="serviceType"/> with an
+ /// implementation of the type specified in <paramref name="implementationType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationType, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <paramref name="serviceType"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationFactory, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedTransient(typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <paramref name="serviceType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register and the implementation to use.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient(
+ this IServiceCollection services,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ return services.AddKeyedTransient(serviceType, serviceKey, serviceType);
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <typeparamref name="TService"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedTransient(typeof(TService), serviceKey);
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <typeparamref name="TService"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedTransient(typeof(TService), serviceKey, implementationFactory);
+ }
+
+ /// <summary>
+ /// Adds a transient service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation" /> using the
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Transient"/>
+ public static IServiceCollection AddKeyedTransient<TService, TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedTransient(typeof(TService), serviceKey, implementationFactory);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <paramref name="serviceType"/> with an
+ /// implementation of the type specified in <paramref name="implementationType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationType, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <paramref name="serviceType"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationFactory, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedScoped(typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <paramref name="serviceType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register and the implementation to use.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped(
+ this IServiceCollection services,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ return services.AddKeyedScoped(serviceType, serviceKey, serviceType);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <typeparamref name="TService"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedScoped(typeof(TService), serviceKey);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <typeparamref name="TService"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedScoped(typeof(TService), serviceKey, implementationFactory);
+ }
+
+ /// <summary>
+ /// Adds a scoped service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation" /> using the
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Scoped"/>
+ public static IServiceCollection AddKeyedScoped<TService, TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedScoped(typeof(TService), serviceKey, implementationFactory);
+ }
+
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <paramref name="serviceType"/> with an
+ /// implementation of the type specified in <paramref name="implementationType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The implementation type of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationType, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <paramref name="serviceType"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return AddKeyed(services, serviceType, serviceKey, implementationFactory, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedSingleton(typeof(TService), serviceKey, typeof(TImplementation));
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <paramref name="serviceType"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register and the implementation to use.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton(
+ this IServiceCollection services,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type serviceType,
+ object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ return services.AddKeyedSingleton(serviceType, serviceKey, serviceType);
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <typeparamref name="TService"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TService>(
+ this IServiceCollection services,
+ object? serviceKey)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+
+ return services.AddKeyedSingleton(typeof(TService), serviceKey, typeof(TService));
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <typeparamref name="TService"/> with a
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedSingleton(typeof(TService), serviceKey, implementationFactory);
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <typeparamref name="TService"/> with an
+ /// implementation type specified in <typeparamref name="TImplementation" /> using the
+ /// factory specified in <paramref name="implementationFactory"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service to add.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation to use.</typeparam>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">The factory that creates the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton<TService, TImplementation>(
+ this IServiceCollection services,
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return services.AddKeyedSingleton(typeof(TService), serviceKey, implementationFactory);
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <paramref name="serviceType"/> with an
+ /// instance specified in <paramref name="implementationInstance"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceType">The type of the service to register.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationInstance">The instance of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton(
+ this IServiceCollection services,
+ Type serviceType,
+ object? serviceKey,
+ object implementationInstance)
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationInstance);
+
+ var serviceDescriptor = new ServiceDescriptor(serviceType, serviceKey, implementationInstance);
+ services.Add(serviceDescriptor);
+ return services;
+ }
+
+ /// <summary>
+ /// Adds a singleton service of the type specified in <typeparamref name="TService" /> with an
+ /// instance specified in <paramref name="implementationInstance"/> to the
+ /// specified <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationInstance">The instance of the service.</param>
+ /// <returns>A reference to this instance after the operation has completed.</returns>
+ /// <seealso cref="ServiceLifetime.Singleton"/>
+ public static IServiceCollection AddKeyedSingleton<TService>(
+ this IServiceCollection services,
+ object? serviceKey,
+ TService implementationInstance)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(services);
+ ThrowHelper.ThrowIfNull(implementationInstance);
+
+ return services.AddKeyedSingleton(typeof(TService), serviceKey, implementationInstance);
+ }
+
+ private static IServiceCollection AddKeyed(
+ IServiceCollection collection,
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
+ ServiceLifetime lifetime)
+ {
+ var descriptor = new ServiceDescriptor(serviceType, serviceKey, implementationType, lifetime);
+ collection.Add(descriptor);
+ return collection;
+ }
+
+ private static IServiceCollection AddKeyed(
+ IServiceCollection collection,
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory,
+ ServiceLifetime lifetime)
+ {
+ var descriptor = new ServiceDescriptor(serviceType, serviceKey, implementationFactory, lifetime);
+ collection.Add(descriptor);
+ return collection;
+ }
+ }
+}
/// <summary>
/// Extension methods for adding services to an <see cref="IServiceCollection" />.
/// </summary>
- public static class ServiceCollectionServiceExtensions
+ public static partial class ServiceCollectionServiceExtensions
{
/// <summary>
/// Adds a transient service of the type specified in <paramref name="serviceType"/> with an
Type serviceType,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
ServiceLifetime lifetime)
- : this(serviceType, lifetime)
+ : this(serviceType, null, implementationType, lifetime)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="implementationType"/>.
+ /// </summary>
+ /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The <see cref="Type"/> implementing the service.</param>
+ /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
+ public ServiceDescriptor(
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
+ ServiceLifetime lifetime)
+ : this(serviceType, serviceKey, lifetime)
{
ThrowHelper.ThrowIfNull(serviceType);
ThrowHelper.ThrowIfNull(implementationType);
- ImplementationType = implementationType;
+ _implementationType = implementationType;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="instance"/>
+ /// as a <see cref="ServiceLifetime.Singleton"/>.
+ /// </summary>
+ /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
+ /// <param name="instance">The instance implementing the service.</param>
+ public ServiceDescriptor(
+ Type serviceType,
+ object instance)
+ : this(serviceType, null, instance)
+ {
}
/// <summary>
/// as a <see cref="ServiceLifetime.Singleton"/>.
/// </summary>
/// <param name="serviceType">The <see cref="Type"/> of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
/// <param name="instance">The instance implementing the service.</param>
public ServiceDescriptor(
Type serviceType,
+ object? serviceKey,
object instance)
- : this(serviceType, ServiceLifetime.Singleton)
+ : this(serviceType, serviceKey, ServiceLifetime.Singleton)
{
ThrowHelper.ThrowIfNull(serviceType);
ThrowHelper.ThrowIfNull(instance);
- ImplementationInstance = instance;
+ _implementationInstance = instance;
}
/// <summary>
Type serviceType,
Func<IServiceProvider, object> factory,
ServiceLifetime lifetime)
- : this(serviceType, lifetime)
+ : this(serviceType, serviceKey: null, lifetime)
+ {
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(factory);
+
+ _implementationFactory = factory;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="ServiceDescriptor"/> with the specified <paramref name="factory"/>.
+ /// </summary>
+ /// <param name="serviceType">The <see cref="Type"/> of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="factory">A factory used for creating service instances.</param>
+ /// <param name="lifetime">The <see cref="ServiceLifetime"/> of the service.</param>
+ public ServiceDescriptor(
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> factory,
+ ServiceLifetime lifetime)
+ : this(serviceType, serviceKey, lifetime)
{
ThrowHelper.ThrowIfNull(serviceType);
ThrowHelper.ThrowIfNull(factory);
- ImplementationFactory = factory;
+ if (serviceKey is null)
+ {
+ // If the key is null, use the same factory signature as non-keyed descriptor
+ Func<IServiceProvider, object> nullKeyedFactory = sp => factory(sp, null);
+ _implementationFactory = nullKeyedFactory;
+ }
+ else
+ {
+ _implementationFactory = factory;
+ }
}
- private ServiceDescriptor(Type serviceType, ServiceLifetime lifetime)
+ private ServiceDescriptor(Type serviceType, object? serviceKey, ServiceLifetime lifetime)
{
Lifetime = lifetime;
ServiceType = serviceType;
+ ServiceKey = serviceKey;
}
/// <summary>
public ServiceLifetime Lifetime { get; }
/// <summary>
+ /// Get the key of the service, if applicable.
+ /// </summary>
+ public object? ServiceKey { get; }
+
+ /// <summary>
/// Gets the <see cref="Type"/> of the service.
/// </summary>
public Type ServiceType { get; }
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+ private Type? _implementationType;
+
+ /// <summary>
+ /// Gets the <see cref="Type"/> that implements the service.
+ /// </summary>
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+ public Type? ImplementationType
+ {
+ get
+ {
+ if (IsKeyedService)
+ {
+ ThrowKeyedDescriptor();
+ }
+ return _implementationType;
+ }
+ }
+
/// <summary>
/// Gets the <see cref="Type"/> that implements the service.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
- public Type? ImplementationType { get; }
+ public Type? KeyedImplementationType
+ {
+ get
+ {
+ if (!IsKeyedService)
+ {
+ ThrowNonKeyedDescriptor();
+ }
+ return _implementationType;
+ }
+ }
+
+ private object? _implementationInstance;
+
+ /// <summary>
+ /// Gets the instance that implements the service.
+ /// </summary>
+ public object? ImplementationInstance
+ {
+ get
+ {
+ if (IsKeyedService)
+ {
+ ThrowKeyedDescriptor();
+ }
+ return _implementationInstance;
+ }
+ }
/// <summary>
/// Gets the instance that implements the service.
/// </summary>
- public object? ImplementationInstance { get; }
+ public object? KeyedImplementationInstance
+ {
+ get
+ {
+ if (!IsKeyedService)
+ {
+ ThrowNonKeyedDescriptor();
+ }
+ return _implementationInstance;
+ }
+ }
+
+ private object? _implementationFactory;
/// <summary>
/// Gets the factory used for creating service instances.
/// </summary>
- public Func<IServiceProvider, object>? ImplementationFactory { get; }
+ public Func<IServiceProvider, object>? ImplementationFactory
+ {
+ get
+ {
+ if (IsKeyedService)
+ {
+ ThrowKeyedDescriptor();
+ }
+ return (Func<IServiceProvider, object>?) _implementationFactory;
+ }
+ }
+
+ /// <summary>
+ /// Gets the factory used for creating Keyed service instances.
+ /// </summary>
+ public Func<IServiceProvider, object?, object>? KeyedImplementationFactory
+ {
+ get
+ {
+ if (!IsKeyedService)
+ {
+ ThrowNonKeyedDescriptor();
+ }
+ return (Func<IServiceProvider, object?, object>?) _implementationFactory;
+ }
+ }
+
+ public bool IsKeyedService => ServiceKey != null;
/// <inheritdoc />
public override string ToString()
{
string? lifetime = $"{nameof(ServiceType)}: {ServiceType} {nameof(Lifetime)}: {Lifetime} ";
- if (ImplementationType != null)
+ if (IsKeyedService)
{
- return lifetime + $"{nameof(ImplementationType)}: {ImplementationType}";
- }
+ lifetime += $"{nameof(ServiceKey)}: {ServiceKey} ";
- if (ImplementationFactory != null)
- {
- return lifetime + $"{nameof(ImplementationFactory)}: {ImplementationFactory.Method}";
+ if (KeyedImplementationType != null)
+ {
+ return lifetime + $"{nameof(KeyedImplementationType)}: {KeyedImplementationType}";
+ }
+
+ if (KeyedImplementationFactory != null)
+ {
+ return lifetime + $"{nameof(KeyedImplementationFactory)}: {KeyedImplementationFactory.Method}";
+ }
+
+ return lifetime + $"{nameof(KeyedImplementationInstance)}: {KeyedImplementationInstance}";
}
+ else
+ {
+ if (ImplementationType != null)
+ {
+ return lifetime + $"{nameof(ImplementationType)}: {ImplementationType}";
+ }
+
+ if (ImplementationFactory != null)
+ {
+ return lifetime + $"{nameof(ImplementationFactory)}: {ImplementationFactory.Method}";
+ }
- return lifetime + $"{nameof(ImplementationInstance)}: {ImplementationInstance}";
+ return lifetime + $"{nameof(ImplementationInstance)}: {ImplementationInstance}";
+ }
}
internal Type GetImplementationType()
{
- if (ImplementationType != null)
- {
- return ImplementationType;
- }
- else if (ImplementationInstance != null)
+ if (ServiceKey == null)
{
- return ImplementationInstance.GetType();
+ if (ImplementationType != null)
+ {
+ return ImplementationType;
+ }
+ else if (ImplementationInstance != null)
+ {
+ return ImplementationInstance.GetType();
+ }
+ else if (ImplementationFactory != null)
+ {
+ Type[]? typeArguments = ImplementationFactory.GetType().GenericTypeArguments;
+
+ Debug.Assert(typeArguments.Length == 2);
+
+ return typeArguments[1];
+ }
}
- else if (ImplementationFactory != null)
+ else
{
- Type[]? typeArguments = ImplementationFactory.GetType().GenericTypeArguments;
+ if (KeyedImplementationType != null)
+ {
+ return KeyedImplementationType;
+ }
+ else if (KeyedImplementationInstance != null)
+ {
+ return KeyedImplementationInstance.GetType();
+ }
+ else if (KeyedImplementationFactory != null)
+ {
+ Type[]? typeArguments = KeyedImplementationFactory.GetType().GenericTypeArguments;
- Debug.Assert(typeArguments.Length == 2);
+ Debug.Assert(typeArguments.Length == 3);
- return typeArguments[1];
+ return typeArguments[2];
+ }
}
- Debug.Assert(false, "ImplementationType, ImplementationInstance or ImplementationFactory must be non null");
+ Debug.Assert(false, "ImplementationType, ImplementationInstance, ImplementationFactory or KeyedImplementationFactory must be non null");
return null;
}
where TService : class
where TImplementation : class, TService
{
- return Describe<TService, TImplementation>(ServiceLifetime.Transient);
+ return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// and the <see cref="ServiceLifetime.Transient"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedTransient<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Transient);
}
/// <summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="service"/> and <paramref name="implementationType"/>
+ /// and the <see cref="ServiceLifetime.Transient"/> lifetime.
+ /// </summary>
+ /// <param name="service">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The type of the implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedTransient(
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Transient"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedTransient<TService, TImplementation>(
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Transient"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedTransient<TService>(object? serviceKey, Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Transient"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="service"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Transient"/> lifetime.
+ /// </summary>
+ /// <param name="service">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedTransient(Type service, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(service, serviceKey, implementationFactory, ServiceLifetime.Transient);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
where TService : class
where TImplementation : class, TService
{
- return Describe<TService, TImplementation>(ServiceLifetime.Scoped);
+ return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedScoped<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Scoped);
}
/// <summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="service"/> and <paramref name="implementationType"/>
+ /// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
+ /// </summary>
+ /// <param name="service">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The type of the implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedScoped(
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedScoped<TService, TImplementation>(
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedScoped<TService>(object? serviceKey, Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="service"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="service"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Scoped"/> lifetime.
+ /// </summary>
+ /// <param name="service">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedScoped(Type service, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(service, serviceKey, implementationFactory, ServiceLifetime.Scoped);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
where TService : class
where TImplementation : class, TService
{
- return Describe<TService, TImplementation>(ServiceLifetime.Singleton);
+ return DescribeKeyed<TService, TImplementation>(null, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+ object? serviceKey)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ return DescribeKeyed<TService, TImplementation>(serviceKey, ServiceLifetime.Singleton);
}
/// <summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="service"/> and <paramref name="implementationType"/>
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <param name="service">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The type of the implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton(
+ Type service,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
+ {
+ ThrowHelper.ThrowIfNull(service);
+ ThrowHelper.ThrowIfNull(implementationType);
+
+ return DescribeKeyed(service, serviceKey, implementationType, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
/// <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <typeparamref name="TImplementation"/>,
+ /// <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <typeparam name="TImplementation">The type of the implementation.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton<TService, TImplementation>(
+ object? serviceKey,
+ Func<IServiceProvider, object?, TImplementation> implementationFactory)
+ where TService : class
+ where TImplementation : class, TService
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton<TService>(
+ object? serviceKey,
+ Func<IServiceProvider, object?, TService> implementationFactory)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(typeof(TService), serviceKey, implementationFactory, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <param name="serviceType">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton(
+ Type serviceType,
+ object? serviceKey,
+ Func<IServiceProvider, object?, object> implementationFactory)
+ {
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationFactory);
+
+ return DescribeKeyed(serviceType, serviceKey, implementationFactory, ServiceLifetime.Singleton);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <typeparamref name="TService"/>, <paramref name="implementationInstance"/>,
/// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
/// </summary>
{
ThrowHelper.ThrowIfNull(implementationInstance);
- return Singleton(typeof(TService), implementationInstance);
+ return Singleton(serviceType: typeof(TService), implementationInstance: implementationInstance);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <typeparamref name="TService"/>, <paramref name="implementationInstance"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <typeparam name="TService">The type of the service.</typeparam>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationInstance">The instance of the implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton<TService>(
+ object? serviceKey,
+ TService implementationInstance)
+ where TService : class
+ {
+ ThrowHelper.ThrowIfNull(implementationInstance);
+
+ return KeyedSingleton(typeof(TService), serviceKey, implementationInstance);
}
/// <summary>
return new ServiceDescriptor(serviceType, implementationInstance);
}
- private static ServiceDescriptor Describe<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(ServiceLifetime lifetime)
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="serviceType"/>, <paramref name="implementationInstance"/>,
+ /// and the <see cref="ServiceLifetime.Singleton"/> lifetime.
+ /// </summary>
+ /// <param name="serviceType">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationInstance">The instance of the implementation.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor KeyedSingleton(
+ Type serviceType,
+ object? serviceKey,
+ object implementationInstance)
+ {
+ ThrowHelper.ThrowIfNull(serviceType);
+ ThrowHelper.ThrowIfNull(implementationInstance);
+
+ return new ServiceDescriptor(serviceType, serviceKey, implementationInstance);
+ }
+
+ private static ServiceDescriptor DescribeKeyed<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(
+ object? serviceKey,
+ ServiceLifetime lifetime)
where TService : class
where TImplementation : class, TService
{
- return Describe(
+ return DescribeKeyed(
typeof(TService),
+ serviceKey,
typeof(TImplementation),
lifetime: lifetime);
}
/// <summary>
/// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="serviceType"/>, <paramref name="implementationType"/>,
+ /// and <paramref name="lifetime"/>.
+ /// </summary>
+ /// <param name="serviceType">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationType">The type of the implementation.</param>
+ /// <param name="lifetime">The lifetime of the service.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor DescribeKeyed(
+ Type serviceType,
+ object? serviceKey,
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
+ ServiceLifetime lifetime)
+ {
+ return new ServiceDescriptor(serviceType, serviceKey, implementationType, lifetime);
+ }
+
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
/// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
/// and <paramref name="lifetime"/>.
/// </summary>
return new ServiceDescriptor(serviceType, implementationFactory, lifetime);
}
+ /// <summary>
+ /// Creates an instance of <see cref="ServiceDescriptor"/> with the specified
+ /// <paramref name="serviceType"/>, <paramref name="implementationFactory"/>,
+ /// and <paramref name="lifetime"/>.
+ /// </summary>
+ /// <param name="serviceType">The type of the service.</param>
+ /// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+ /// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
+ /// <param name="lifetime">The lifetime of the service.</param>
+ /// <returns>A new instance of <see cref="ServiceDescriptor"/>.</returns>
+ public static ServiceDescriptor DescribeKeyed(Type serviceType, object? serviceKey, Func<IServiceProvider, object?, object> implementationFactory, ServiceLifetime lifetime)
+ {
+ return new ServiceDescriptor(serviceType, serviceKey, implementationFactory, lifetime);
+ }
+
private string DebuggerToString()
{
string debugText = $@"Lifetime = {Lifetime}, ServiceType = ""{ServiceType.FullName}""";
// Either implementation type, factory or instance is set.
- if (ImplementationType != null)
- {
- debugText += $@", ImplementationType = ""{ImplementationType.FullName}""";
- }
- else if (ImplementationFactory != null)
+ if (IsKeyedService)
{
- debugText += $@", ImplementationFactory = {ImplementationFactory.Method}";
+ debugText += $@", ServiceKey = ""{ServiceKey}""";
+ if (KeyedImplementationType != null)
+ {
+ debugText += $@", KeyedImplementationType = ""{KeyedImplementationType.FullName}""";
+ }
+ else if (KeyedImplementationFactory != null)
+ {
+ debugText += $@", KeyedImplementationFactory = {KeyedImplementationFactory.Method}";
+ }
+ else
+ {
+ debugText += $@", KeyedImplementationInstance = {KeyedImplementationInstance}";
+ }
}
else
{
- debugText += $@", ImplementationInstance = {ImplementationInstance}";
+ if (ImplementationType != null)
+ {
+ debugText += $@", ImplementationType = ""{ImplementationType.FullName}""";
+ }
+ else if (ImplementationFactory != null)
+ {
+ debugText += $@", ImplementationFactory = {ImplementationFactory.Method}";
+ }
+ else
+ {
+ debugText += $@", ImplementationInstance = {ImplementationInstance}";
+ }
}
return debugText;
}
+
+ private static void ThrowKeyedDescriptor() => throw new InvalidOperationException(SR.KeyedDescriptorMisuse);
+
+ private static void ThrowNonKeyedDescriptor() => throw new InvalidOperationException(SR.NonKeyedDescriptorMisuse);
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class ServiceKeyAttribute : Attribute
+ {
+ }
+}
--- /dev/null
+// 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;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ /// <summary>
+ /// Extension methods for getting services from an <see cref="IServiceProvider" />.
+ /// </summary>
+ public static class ServiceProviderKeyedServiceExtensions
+ {
+ /// <summary>
+ /// Get service of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <typeparam name="T">The type of service object to get.</typeparam>
+ /// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the service object from.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns>A service object of type <typeparamref name="T"/> or null if there is no such service.</returns>
+ public static T? GetKeyedService<T>(this IServiceProvider provider, object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(provider);
+
+ if (provider is IKeyedServiceProvider keyedServiceProvider)
+ {
+ return (T?)keyedServiceProvider.GetKeyedService(typeof(T), serviceKey);
+ }
+
+ throw new InvalidOperationException(SR.KeyedServicesNotSupported);
+ }
+
+ /// <summary>
+ /// Get service of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the service object from.</param>
+ /// <param name="serviceType">An object that specifies the type of service object to get.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns>A service object of type <paramref name="serviceType"/>.</returns>
+ /// <exception cref="System.InvalidOperationException">There is no service of type <paramref name="serviceType"/>.</exception>
+ public static object GetRequiredKeyedService(this IServiceProvider provider, Type serviceType, object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(provider);
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ if (provider is IKeyedServiceProvider requiredServiceSupportingProvider)
+ {
+ return requiredServiceSupportingProvider.GetRequiredKeyedService(serviceType, serviceKey);
+ }
+
+ throw new InvalidOperationException(SR.KeyedServicesNotSupported);
+ }
+
+ /// <summary>
+ /// Get service of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <typeparam name="T">The type of service object to get.</typeparam>
+ /// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the service object from.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns>A service object of type <typeparamref name="T"/>.</returns>
+ /// <exception cref="System.InvalidOperationException">There is no service of type <typeparamref name="T"/>.</exception>
+ public static T GetRequiredKeyedService<T>(this IServiceProvider provider, object? serviceKey) where T : notnull
+ {
+ ThrowHelper.ThrowIfNull(provider);
+
+ return (T)provider.GetRequiredKeyedService(typeof(T), serviceKey);
+ }
+
+ /// <summary>
+ /// Get an enumeration of services of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <typeparam name="T">The type of service object to get.</typeparam>
+ /// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the services from.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns>An enumeration of services of type <typeparamref name="T"/>.</returns>
+ public static IEnumerable<T> GetKeyedServices<T>(this IServiceProvider provider, object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(provider);
+
+ return provider.GetRequiredKeyedService<IEnumerable<T>>(serviceKey);
+ }
+
+ /// <summary>
+ /// Get an enumeration of services of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <param name="provider">The <see cref="IServiceProvider"/> to retrieve the services from.</param>
+ /// <param name="serviceType">An object that specifies the type of service object to get.</param>
+ /// <param name="serviceKey">An object that specifies the key of service object to get.</param>
+ /// <returns>An enumeration of services of type <paramref name="serviceType"/>.</returns>
+ [RequiresDynamicCode("The native code for an IEnumerable<serviceType> might not be available at runtime.")]
+ public static IEnumerable<object?> GetKeyedServices(this IServiceProvider provider, Type serviceType, object? serviceKey)
+ {
+ ThrowHelper.ThrowIfNull(provider);
+ ThrowHelper.ThrowIfNull(serviceType);
+
+ Type? genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
+ return (IEnumerable<object>)provider.GetRequiredKeyedService(genericEnumerable, serviceKey);
+ }
+ }
+}
--- /dev/null
+// 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 Xunit;
+using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace Microsoft.Extensions.DependencyInjection.Specification
+{
+ public abstract partial class KeyedDependencyInjectionSpecificationTests
+ {
+ protected abstract IServiceProvider CreateServiceProvider(IServiceCollection collection);
+
+ [Fact]
+ public void ResolveKeyedService()
+ {
+ var service1 = new Service();
+ var service2 = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>("service1", service1);
+ serviceCollection.AddKeyedSingleton<IService>("service2", service2);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ Assert.Same(service1, provider.GetKeyedService<IService>("service1"));
+ Assert.Same(service2, provider.GetKeyedService<IService>("service2"));
+ }
+
+ [Fact]
+ public void ResolveNullKeyedService()
+ {
+ var service1 = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>(null, service1);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ var nonKeyed = provider.GetService<IService>();
+ var nullKey = provider.GetKeyedService<IService>(null);
+
+ Assert.Same(service1, nonKeyed);
+ Assert.Same(service1, nullKey);
+ }
+
+ [Fact]
+ public void ResolveNonKeyedService()
+ {
+ var service1 = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddSingleton<IService>(service1);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ var nonKeyed = provider.GetService<IService>();
+ var nullKey = provider.GetKeyedService<IService>(null);
+
+ Assert.Same(service1, nonKeyed);
+ Assert.Same(service1, nullKey);
+ }
+
+ [Fact]
+ public void ResolveKeyedOpenGenericService()
+ {
+ var collection = new ServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeOpenGenericService<>), "my-service", typeof(FakeOpenGenericService<>));
+ collection.AddSingleton<IFakeSingletonService, FakeService>();
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var genericService = provider.GetKeyedService<IFakeOpenGenericService<IFakeSingletonService>>("my-service");
+ var singletonService = provider.GetService<IFakeSingletonService>();
+
+ // Assert
+ Assert.Same(singletonService, genericService.Value);
+ }
+
+ [Fact]
+ public void ResolveKeyedServices()
+ {
+ var service1 = new Service();
+ var service2 = new Service();
+ var service3 = new Service();
+ var service4 = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>("first-service", service1);
+ serviceCollection.AddKeyedSingleton<IService>("service", service2);
+ serviceCollection.AddKeyedSingleton<IService>("service", service3);
+ serviceCollection.AddKeyedSingleton<IService>("service", service4);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ var firstSvc = provider.GetKeyedServices<IService>("first-service").ToList();
+ Assert.Single(firstSvc);
+ Assert.Same(service1, firstSvc[0]);
+
+ var services = provider.GetKeyedServices<IService>("service").ToList();
+ Assert.Equal(new[] { service2, service3, service4 }, services);
+ }
+
+ [Fact]
+ public void ResolveKeyedGenericServices()
+ {
+ var service1 = new FakeService();
+ var service2 = new FakeService();
+ var service3 = new FakeService();
+ var service4 = new FakeService();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>("first-service", service1);
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>("service", service2);
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>("service", service3);
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>("service", service4);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ var firstSvc = provider.GetKeyedServices<IFakeOpenGenericService<PocoClass>>("first-service").ToList();
+ Assert.Single(firstSvc);
+ Assert.Same(service1, firstSvc[0]);
+
+ var services = provider.GetKeyedServices<IFakeOpenGenericService<PocoClass>>("service").ToList();
+ Assert.Equal(new[] { service2, service3, service4 }, services);
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonInstance()
+ {
+ var service = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>("service1", service);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ Assert.Same(service, provider.GetKeyedService<IService>("service1"));
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonInstanceWithKeyInjection()
+ {
+ var serviceKey = "this-is-my-service";
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService, Service>(serviceKey);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ var svc = provider.GetKeyedService<IService>(serviceKey);
+ Assert.NotNull(svc);
+ Assert.Equal(serviceKey, svc.ToString());
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonInstanceWithAnyKey()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService, Service>(KeyedService.AnyKey);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+
+ var serviceKey1 = "some-key";
+ var svc1 = provider.GetKeyedService<IService>(serviceKey1);
+ Assert.NotNull(svc1);
+ Assert.Equal(serviceKey1, svc1.ToString());
+
+ var serviceKey2 = "some-other-key";
+ var svc2 = provider.GetKeyedService<IService>(serviceKey2);
+ Assert.NotNull(svc2);
+ Assert.Equal(serviceKey2, svc2.ToString());
+ }
+
+ [Fact]
+ public void ResolveKeyedServicesSingletonInstanceWithAnyKey()
+ {
+ var service1 = new FakeService();
+ var service2 = new FakeService();
+
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>(KeyedService.AnyKey, service1);
+ serviceCollection.AddKeyedSingleton<IFakeOpenGenericService<PocoClass>>("some-key", service2);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ var services = provider.GetKeyedServices<IFakeOpenGenericService<PocoClass>>("some-key").ToList();
+ Assert.Equal(new[] { service1, service2 }, services);
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonInstanceWithKeyedParameter()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService, Service>("service1");
+ serviceCollection.AddKeyedSingleton<IService, Service>("service2");
+ serviceCollection.AddSingleton<OtherService>();
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ var svc = provider.GetService<OtherService>();
+ Assert.NotNull(svc);
+ Assert.Equal("service1", svc.Service1.ToString());
+ Assert.Equal("service2", svc.Service2.ToString());
+ }
+
+ [Fact]
+ public void CreateServiceWithKeyedParameter()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddSingleton<IService, Service>();
+ serviceCollection.AddKeyedSingleton<IService, Service>("service1");
+ serviceCollection.AddKeyedSingleton<IService, Service>("service2");
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<OtherService>());
+ var svc = ActivatorUtilities.CreateInstance<OtherService>(provider);
+ Assert.NotNull(svc);
+ Assert.Equal("service1", svc.Service1.ToString());
+ Assert.Equal("service2", svc.Service2.ToString());
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonFactory()
+ {
+ var service = new Service();
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>("service1", (sp, key) => service);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ Assert.Same(service, provider.GetKeyedService<IService>("service1"));
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonFactoryWithAnyKey()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService>(KeyedService.AnyKey, (sp, key) => new Service((string)key));
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+
+ for (int i=0; i<3; i++)
+ {
+ var key = "service" + i;
+ var s1 = provider.GetKeyedService<IService>(key);
+ var s2 = provider.GetKeyedService<IService>(key);
+ Assert.Same(s1, s2);
+ Assert.Equal(key, s1.ToString());
+ }
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonFactoryWithAnyKeyIgnoreWrongType()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedTransient<IService, ServiceWithIntKey>(KeyedService.AnyKey);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ Assert.NotNull(provider.GetKeyedService<IService>(87));
+ Assert.ThrowsAny<InvalidOperationException>(() => provider.GetKeyedService<IService>(new object()));
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceSingletonType()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedSingleton<IService, Service>("service1");
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ Assert.Equal(typeof(Service), provider.GetKeyedService<IService>("service1")!.GetType());
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceTransientFactory()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedTransient<IService>("service1", (sp, key) => new Service(key as string));
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ var first = provider.GetKeyedService<IService>("service1");
+ var second = provider.GetKeyedService<IService>("service1");
+ Assert.NotSame(first, second);
+ Assert.Equal("service1", first.ToString());
+ Assert.Equal("service1", second.ToString());
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceTransientType()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedTransient<IService, Service>("service1");
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ var first = provider.GetKeyedService<IService>("service1");
+ var second = provider.GetKeyedService<IService>("service1");
+ Assert.NotSame(first, second);
+ }
+
+ [Fact]
+ public void ResolveKeyedServiceTransientTypeWithAnyKey()
+ {
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddKeyedTransient<IService, Service>(KeyedService.AnyKey);
+
+ var provider = CreateServiceProvider(serviceCollection);
+
+ Assert.Null(provider.GetService<IService>());
+ var first = provider.GetKeyedService<IService>("service1");
+ var second = provider.GetKeyedService<IService>("service1");
+ Assert.NotSame(first, second);
+ }
+
+ internal interface IService { }
+
+ internal class Service : IService
+ {
+ private readonly string _id;
+
+ public Service() => _id = Guid.NewGuid().ToString();
+
+ public Service([ServiceKey] string id) => _id = id;
+
+ public override string? ToString() => _id;
+ }
+
+ internal class OtherService
+ {
+ public OtherService(
+ [FromKeyedServices("service1")] IService service1,
+ [FromKeyedServices("service2")] IService service2)
+ {
+ Service1 = service1;
+ Service2 = service2;
+ }
+
+ public IService Service1 { get; }
+
+ public IService Service2 { get; }
+ }
+
+ internal class ServiceWithIntKey : IService
+ {
+ private readonly int _id;
+
+ public ServiceWithIntKey([ServiceKey] int id) => _id = id;
+ }
+ }
+}
--- /dev/null
+// 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.Text;
+using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection.Specification
+{
+ public abstract partial class KeyedDependencyInjectionSpecificationTests
+ {
+ public virtual bool SupportsIServiceProviderIsKeyedService => true;
+
+ [Fact]
+ public void ExplicitServiceRegistrationWithIsKeyedService()
+ {
+ if (!SupportsIServiceProviderIsKeyedService)
+ {
+ return;
+ }
+
+ // Arrange
+ var key = new object();
+ var collection = new TestServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeService), key, typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsKeyedService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IFakeService), key));
+ Assert.False(serviceProviderIsService.IsKeyedService(typeof(FakeService), new object()));
+ }
+
+ [Fact]
+ public void OpenGenericsWithIsKeyedService()
+ {
+ if (!SupportsIServiceProviderIsKeyedService)
+ {
+ return;
+ }
+
+ // Arrange
+ var key = new object();
+ var collection = new TestServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeOpenGenericService<>), key, typeof(FakeOpenGenericService<>));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsKeyedService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IFakeOpenGenericService<IFakeService>), key));
+ Assert.False(serviceProviderIsService.IsKeyedService(typeof(IFakeOpenGenericService<>), new object()));
+ }
+
+ [Fact]
+ public void ClosedGenericsWithIsKeyedService()
+ {
+ if (!SupportsIServiceProviderIsKeyedService)
+ {
+ return;
+ }
+
+ // Arrange
+ var key = new object();
+ var collection = new TestServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeOpenGenericService<IFakeService>), key, typeof(FakeOpenGenericService<IFakeService>));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsKeyedService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IFakeOpenGenericService<IFakeService>), key));
+ }
+
+ [Fact]
+ public void IEnumerableWithIsKeyedServiceAlwaysReturnsTrue()
+ {
+ if (!SupportsIServiceProviderIsKeyedService)
+ {
+ return;
+ }
+
+ // Arrange
+ var key = new object();
+ var collection = new TestServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeService), key, typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsKeyedService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IEnumerable<IFakeService>), key));
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IEnumerable<FakeService>), key));
+ Assert.False(serviceProviderIsService.IsKeyedService(typeof(IEnumerable<>), new object()));
+ }
+
+ [Fact]
+ public void NonKeyedServiceWithIsKeyedService()
+ {
+ if (!SupportsIServiceProviderIsKeyedService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddKeyedTransient(typeof(IFakeService), null, typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsKeyedService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsKeyedService(typeof(IFakeService), null));
+ }
+ }
+}
public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.DependencyInjection.ServiceProviderOptions options) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, bool validateScopes) { throw null; }
}
- public sealed partial class ServiceProvider : System.IAsyncDisposable, System.IDisposable, System.IServiceProvider
+ public sealed partial class ServiceProvider : Microsoft.Extensions.DependencyInjection.IKeyedServiceProvider, System.IAsyncDisposable, System.IDisposable, System.IServiceProvider
{
internal ServiceProvider() { }
public void Dispose() { }
public System.Threading.Tasks.ValueTask DisposeAsync() { throw null; }
+ public object? GetKeyedService(System.Type serviceType, object? serviceKey) { throw null; }
+ public object GetRequiredKeyedService(System.Type serviceType, object? serviceKey) { throw null; }
public object? GetService(System.Type serviceType) { throw null; }
}
public partial class ServiceProviderOptions
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Tracing;
using System.Linq.Expressions;
builder.Append(descriptor.Lifetime);
builder.Append("\", ");
- if (descriptor.ImplementationType is not null)
+ if (descriptor.HasImplementationType())
{
builder.Append("\"implementationType\": \"");
- builder.Append(descriptor.ImplementationType);
+ builder.Append(descriptor.GetImplementationType());
}
- else if (descriptor.ImplementationFactory is not null)
+ else if (!descriptor.IsKeyedService && descriptor.ImplementationFactory != null)
{
builder.Append("\"implementationFactory\": \"");
builder.Append(descriptor.ImplementationFactory.Method);
}
- else if (descriptor.ImplementationInstance is not null)
+ else if (descriptor.IsKeyedService && descriptor.KeyedImplementationFactory != null)
{
+ builder.Append("\"implementationFactory\": \"");
+ builder.Append(descriptor.KeyedImplementationFactory.Method);
+ }
+ else if (descriptor.HasImplementationInstance())
+ {
+ object? instance = descriptor.GetImplementationInstance();
+ Debug.Assert(instance != null, "descriptor.ImplementationInstance != null");
builder.Append("\"implementationInstance\": \"");
- builder.Append(descriptor.ImplementationInstance.GetType());
+ builder.Append(instance.GetType());
builder.Append(" (instance)");
}
else
<data name="AotCannotCreateGenericValueType" xml:space="preserve">
<value>Unable to create a generic service for type '{0}' because '{1}' is a ValueType. Native code to support creating generic services might not be available with native AOT.</value>
</data>
+ <data name="NoServiceRegistered" xml:space="preserve">
+ <value>No service for type '{0}' has been registered.</value>
+ </data>
+ <data name="InvalidServiceKeyType" xml:space="preserve">
+ <value>The type of the key used for lookup doesn't match the type in the constructor parameter with the ServiceKey attribute.</value>
+ </data>
</root>
\ No newline at end of file
{
internal sealed class CallSiteChain
{
- private readonly Dictionary<Type, ChainItemInfo> _callSiteChain;
+ private readonly Dictionary<ServiceIdentifier, ChainItemInfo> _callSiteChain;
public CallSiteChain()
{
- _callSiteChain = new Dictionary<Type, ChainItemInfo>();
+ _callSiteChain = new Dictionary<ServiceIdentifier, ChainItemInfo>();
}
- public void CheckCircularDependency(Type serviceType)
+ public void CheckCircularDependency(ServiceIdentifier serviceIdentifier)
{
- if (_callSiteChain.ContainsKey(serviceType))
+ if (_callSiteChain.ContainsKey(serviceIdentifier))
{
- throw new InvalidOperationException(CreateCircularDependencyExceptionMessage(serviceType));
+ throw new InvalidOperationException(CreateCircularDependencyExceptionMessage(serviceIdentifier));
}
}
- public void Remove(Type serviceType)
+ public void Remove(ServiceIdentifier serviceIdentifier)
{
- _callSiteChain.Remove(serviceType);
+ _callSiteChain.Remove(serviceIdentifier);
}
- public void Add(Type serviceType, Type? implementationType = null)
+ public void Add(ServiceIdentifier serviceIdentifier, Type? implementationType = null)
{
- _callSiteChain[serviceType] = new ChainItemInfo(_callSiteChain.Count, implementationType);
+ _callSiteChain[serviceIdentifier] = new ChainItemInfo(_callSiteChain.Count, implementationType);
}
- private string CreateCircularDependencyExceptionMessage(Type type)
+ private string CreateCircularDependencyExceptionMessage(ServiceIdentifier serviceIdentifier)
{
var messageBuilder = new StringBuilder();
- messageBuilder.Append(SR.Format(SR.CircularDependencyException, TypeNameHelper.GetTypeDisplayName(type)));
+ messageBuilder.Append(SR.Format(SR.CircularDependencyException, TypeNameHelper.GetTypeDisplayName(serviceIdentifier.ServiceType)));
messageBuilder.AppendLine();
- AppendResolutionPath(messageBuilder, type);
+ AppendResolutionPath(messageBuilder, serviceIdentifier);
return messageBuilder.ToString();
}
- private void AppendResolutionPath(StringBuilder builder, Type currentlyResolving)
+ private void AppendResolutionPath(StringBuilder builder, ServiceIdentifier currentlyResolving)
{
- var ordered = new List<KeyValuePair<Type, ChainItemInfo>>(_callSiteChain);
+ var ordered = new List<KeyValuePair<ServiceIdentifier, ChainItemInfo>>(_callSiteChain);
ordered.Sort((a, b) => a.Value.Order.CompareTo(b.Value.Order));
- foreach (KeyValuePair<Type, ChainItemInfo> pair in ordered)
+ foreach (KeyValuePair<ServiceIdentifier, ChainItemInfo> pair in ordered)
{
- Type serviceType = pair.Key;
+ ServiceIdentifier serviceIdentifier = pair.Key;
Type? implementationType = pair.Value.ImplementationType;
- if (implementationType == null || serviceType == implementationType)
+ if (implementationType == null || serviceIdentifier.ServiceType == implementationType)
{
- builder.Append(TypeNameHelper.GetTypeDisplayName(serviceType));
+ builder.Append(TypeNameHelper.GetTypeDisplayName(serviceIdentifier.ServiceType));
}
else
{
- builder.Append(TypeNameHelper.GetTypeDisplayName(serviceType))
+ builder.Append(TypeNameHelper.GetTypeDisplayName(serviceIdentifier.ServiceType))
.Append('(')
.Append(TypeNameHelper.GetTypeDisplayName(implementationType))
.Append(')');
builder.Append(" -> ");
}
- builder.Append(TypeNameHelper.GetTypeDisplayName(currentlyResolving));
+ builder.Append(TypeNameHelper.GetTypeDisplayName(currentlyResolving.ServiceType));
}
private readonly struct ChainItemInfo
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
- internal sealed class CallSiteFactory : IServiceProviderIsService
+ internal sealed class CallSiteFactory : IServiceProviderIsService, IServiceProviderIsKeyedService
{
private const int DefaultSlot = 0;
private readonly ServiceDescriptor[] _descriptors;
private readonly ConcurrentDictionary<ServiceCacheKey, ServiceCallSite> _callSiteCache = new ConcurrentDictionary<ServiceCacheKey, ServiceCallSite>();
- private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new Dictionary<Type, ServiceDescriptorCacheItem>();
- private readonly ConcurrentDictionary<Type, object> _callSiteLocks = new ConcurrentDictionary<Type, object>();
+ private readonly Dictionary<ServiceIdentifier, ServiceDescriptorCacheItem> _descriptorLookup = new Dictionary<ServiceIdentifier, ServiceDescriptorCacheItem>();
+ private readonly ConcurrentDictionary<ServiceIdentifier, object> _callSiteLocks = new ConcurrentDictionary<ServiceIdentifier, object>();
private readonly StackGuard _stackGuard;
Type serviceType = descriptor.ServiceType;
if (serviceType.IsGenericTypeDefinition)
{
- Type? implementationType = descriptor.ImplementationType;
+ Type? implementationType = descriptor.GetImplementationType();
if (implementationType == null || !implementationType.IsGenericTypeDefinition)
{
ValidateTrimmingAnnotations(serviceType, serviceTypeGenericArguments, implementationType, implementationTypeGenericArguments);
}
}
- else if (descriptor.ImplementationInstance == null && descriptor.ImplementationFactory == null)
+ else if (!descriptor.HasImplementationInstance() && !descriptor.HasImplementationFactory())
{
- Debug.Assert(descriptor.ImplementationType != null);
- Type implementationType = descriptor.ImplementationType;
+ Type? implementationType = descriptor.GetImplementationType();
+ Debug.Assert(implementationType != null);
if (implementationType.IsGenericTypeDefinition ||
implementationType.IsAbstract ||
}
}
- Type cacheKey = serviceType;
+ var cacheKey = ServiceIdentifier.FromDescriptor(descriptor);
_descriptorLookup.TryGetValue(cacheKey, out ServiceDescriptorCacheItem cacheItem);
_descriptorLookup[cacheKey] = cacheItem.Add(descriptor);
}
// For unit testing
internal int? GetSlot(ServiceDescriptor serviceDescriptor)
{
- if (_descriptorLookup.TryGetValue(serviceDescriptor.ServiceType, out ServiceDescriptorCacheItem item))
+ if (_descriptorLookup.TryGetValue(ServiceIdentifier.FromDescriptor(serviceDescriptor), out ServiceDescriptorCacheItem item))
{
return item.GetSlot(serviceDescriptor);
}
return null;
}
- internal ServiceCallSite? GetCallSite(Type serviceType, CallSiteChain callSiteChain) =>
- _callSiteCache.TryGetValue(new ServiceCacheKey(serviceType, DefaultSlot), out ServiceCallSite? site) ? site :
- CreateCallSite(serviceType, callSiteChain);
+ internal ServiceCallSite? GetCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain) =>
+ _callSiteCache.TryGetValue(new ServiceCacheKey(serviceIdentifier, DefaultSlot), out ServiceCallSite? site) ? site :
+ CreateCallSite(serviceIdentifier, callSiteChain);
internal ServiceCallSite? GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain)
{
- if (_descriptorLookup.TryGetValue(serviceDescriptor.ServiceType, out ServiceDescriptorCacheItem descriptor))
+ var serviceIdentifier = ServiceIdentifier.FromDescriptor(serviceDescriptor);
+ if (_descriptorLookup.TryGetValue(serviceIdentifier, out ServiceDescriptorCacheItem descriptor))
{
- return TryCreateExact(serviceDescriptor, serviceDescriptor.ServiceType, callSiteChain, descriptor.GetSlot(serviceDescriptor));
+ return TryCreateExact(serviceDescriptor, serviceIdentifier, callSiteChain, descriptor.GetSlot(serviceDescriptor));
}
Debug.Fail("_descriptorLookup didn't contain requested serviceDescriptor");
return null;
}
- private ServiceCallSite? CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
+ private ServiceCallSite? CreateCallSite(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
if (!_stackGuard.TryEnterOnCurrentStack())
{
- return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceType, callSiteChain);
+ return _stackGuard.RunOnEmptyStack(CreateCallSite, serviceIdentifier, callSiteChain);
}
// We need to lock the resolution process for a single service type at a time:
// This is to make sure we can safely store singleton values on the callsites themselves
- var callsiteLock = _callSiteLocks.GetOrAdd(serviceType, static _ => new object());
+ var callsiteLock = _callSiteLocks.GetOrAdd(serviceIdentifier, static _ => new object());
lock (callsiteLock)
{
- callSiteChain.CheckCircularDependency(serviceType);
+ callSiteChain.CheckCircularDependency(serviceIdentifier);
- ServiceCallSite? callSite = TryCreateExact(serviceType, callSiteChain) ??
- TryCreateOpenGeneric(serviceType, callSiteChain) ??
- TryCreateEnumerable(serviceType, callSiteChain);
+ ServiceCallSite? callSite = TryCreateExact(serviceIdentifier, callSiteChain) ??
+ TryCreateOpenGeneric(serviceIdentifier, callSiteChain) ??
+ TryCreateEnumerable(serviceIdentifier, callSiteChain);
return callSite;
}
}
- private ServiceCallSite? TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
+ private ServiceCallSite? TryCreateExact(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
- if (_descriptorLookup.TryGetValue(serviceType, out ServiceDescriptorCacheItem descriptor))
+ if (_descriptorLookup.TryGetValue(serviceIdentifier, out ServiceDescriptorCacheItem descriptor))
{
- return TryCreateExact(descriptor.Last, serviceType, callSiteChain, DefaultSlot);
+ return TryCreateExact(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot);
+ }
+
+ if (serviceIdentifier.ServiceKey != null)
+ {
+ // Check if there is a registration with KeyedService.AnyKey
+ var catchAllIdentifier = new ServiceIdentifier(KeyedService.AnyKey, serviceIdentifier.ServiceType);
+ if (_descriptorLookup.TryGetValue(catchAllIdentifier, out descriptor))
+ {
+ return TryCreateExact(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot);
+ }
}
return null;
}
- private ServiceCallSite? TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain)
+ private ServiceCallSite? TryCreateOpenGeneric(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
- if (serviceType.IsConstructedGenericType
- && _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out ServiceDescriptorCacheItem descriptor))
+ if (serviceIdentifier.IsConstructedGenericType)
{
- return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot, true);
+ var genericIdentifier = serviceIdentifier.GetGenericTypeDefinition();
+ if (_descriptorLookup.TryGetValue(genericIdentifier, out ServiceDescriptorCacheItem descriptor))
+ {
+ return TryCreateOpenGeneric(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot, true);
+ }
+
+ if (serviceIdentifier.ServiceKey != null)
+ {
+ // Check if there is a registration with KeyedService.AnyKey
+ var catchAllIdentifier = new ServiceIdentifier(KeyedService.AnyKey, genericIdentifier.ServiceType);
+ if (_descriptorLookup.TryGetValue(catchAllIdentifier, out descriptor))
+ {
+ return TryCreateOpenGeneric(descriptor.Last, serviceIdentifier, callSiteChain, DefaultSlot, true);
+ }
+ }
}
return null;
}
- private ServiceCallSite? TryCreateEnumerable(Type serviceType, CallSiteChain callSiteChain)
+ private ServiceCallSite? TryCreateEnumerable(ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain)
{
- ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceType, DefaultSlot);
+ ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, DefaultSlot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
try
{
- callSiteChain.Add(serviceType);
+ callSiteChain.Add(serviceIdentifier);
+
+ var serviceType = serviceIdentifier.ServiceType;
if (!serviceType.IsConstructedGenericType ||
serviceType.GetGenericTypeDefinition() != typeof(IEnumerable<>))
}
Type itemType = serviceType.GenericTypeArguments[0];
+ var cacheKey = new ServiceIdentifier(serviceIdentifier.ServiceKey, itemType);
if (ServiceProvider.VerifyAotCompatibility && itemType.IsValueType)
{
// NativeAOT apps are not able to make Enumerable of ValueType services
// If item type is not generic we can safely use descriptor cache
if (!itemType.IsConstructedGenericType &&
- _descriptorLookup.TryGetValue(itemType, out ServiceDescriptorCacheItem descriptors))
+ _descriptorLookup.TryGetValue(cacheKey, out ServiceDescriptorCacheItem descriptors))
{
callSites = new ServiceCallSite[descriptors.Count];
for (int i = 0; i < descriptors.Count; i++)
// Last service should get slot 0
int slot = descriptors.Count - i - 1;
// There may not be any open generics here
- ServiceCallSite? callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot);
+ ServiceCallSite? callSite = TryCreateExact(descriptor, cacheKey, callSiteChain, slot);
Debug.Assert(callSite != null);
cacheLocation = GetCommonCacheLocation(cacheLocation, callSite.Cache.Location);
int slot = 0;
for (int i = _descriptors.Length - 1; i >= 0; i--)
{
- if (TryCreateExact(_descriptors[i], itemType, callSiteChain, slot) is { } callSite)
+ if (KeysMatch(_descriptors[i].ServiceKey, cacheKey.ServiceKey))
{
- AddCallSite(callSite, i);
+ if (TryCreateExact(_descriptors[i], cacheKey, callSiteChain, slot) is { } callSite)
+ {
+ AddCallSite(callSite, i);
+ }
}
}
for (int i = _descriptors.Length - 1; i >= 0; i--)
{
- if (TryCreateOpenGeneric(_descriptors[i], itemType, callSiteChain, slot, throwOnConstraintViolation: false) is { } callSite)
+ if (KeysMatch(_descriptors[i].ServiceKey, cacheKey.ServiceKey))
{
- AddCallSite(callSite, i);
+ if (TryCreateOpenGeneric(_descriptors[i], cacheKey, callSiteChain, slot, throwOnConstraintViolation: false) is { } callSite)
+ {
+ AddCallSite(callSite, i);
+ }
}
}
}
finally
{
- callSiteChain.Remove(serviceType);
+ callSiteChain.Remove(serviceIdentifier);
}
}
return (CallSiteResultCacheLocation)Math.Max((int)locationA, (int)locationB);
}
- private ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
+ private ServiceCallSite? TryCreateExact(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot)
{
- if (serviceType == descriptor.ServiceType)
+ if (serviceIdentifier.ServiceType == descriptor.ServiceType)
{
- ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceType, slot);
+ ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
}
ServiceCallSite callSite;
- var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
- if (descriptor.ImplementationInstance != null)
+ var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);
+ if (descriptor.HasImplementationInstance())
{
- callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
+ callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.GetImplementationInstance());
}
- else if (descriptor.ImplementationFactory != null)
+ else if (!descriptor.IsKeyedService && descriptor.ImplementationFactory != null)
{
callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
}
- else if (descriptor.ImplementationType != null)
+ else if (descriptor.IsKeyedService && descriptor.KeyedImplementationFactory != null)
+ {
+ callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, serviceIdentifier.ServiceKey!, descriptor.KeyedImplementationFactory);
+ }
+ else if (descriptor.HasImplementationType())
{
- callSite = CreateConstructorCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
+ callSite = CreateConstructorCallSite(lifetime, serviceIdentifier, descriptor.GetImplementationType()!, callSiteChain);
}
else
{
throw new InvalidOperationException(SR.InvalidServiceDescriptor);
}
+ callSite.Key = descriptor.ServiceKey;
return _callSiteCache[callSiteKey] = callSite;
}
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " +
"this method ensures the generic types being created aren't using ValueTypes.")]
- private ServiceCallSite? TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation)
+ private ServiceCallSite? TryCreateOpenGeneric(ServiceDescriptor descriptor, ServiceIdentifier serviceIdentifier, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation)
{
- if (serviceType.IsConstructedGenericType &&
- serviceType.GetGenericTypeDefinition() == descriptor.ServiceType)
+ if (serviceIdentifier.IsConstructedGenericType &&
+ serviceIdentifier.ServiceType.GetGenericTypeDefinition() == descriptor.ServiceType)
{
- ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceType, slot);
+ ServiceCacheKey callSiteKey = new ServiceCacheKey(serviceIdentifier, slot);
if (_callSiteCache.TryGetValue(callSiteKey, out ServiceCallSite? serviceCallSite))
{
return serviceCallSite;
}
- Debug.Assert(descriptor.ImplementationType != null, "descriptor.ImplementationType != null");
- var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
+ Type? implementationType = descriptor.GetImplementationType();
+ Debug.Assert(implementationType != null, "descriptor.ImplementationType != null");
+ var lifetime = new ResultCache(descriptor.Lifetime, serviceIdentifier, slot);
Type closedType;
try
{
- Type[] genericTypeArguments = serviceType.GenericTypeArguments;
+ Type[] genericTypeArguments = serviceIdentifier.ServiceType.GenericTypeArguments;
if (ServiceProvider.VerifyAotCompatibility)
{
- VerifyOpenGenericAotCompatibility(serviceType, genericTypeArguments);
+ VerifyOpenGenericAotCompatibility(serviceIdentifier.ServiceType, genericTypeArguments);
}
- closedType = descriptor.ImplementationType.MakeGenericType(genericTypeArguments);
+ closedType = implementationType.MakeGenericType(genericTypeArguments);
}
catch (ArgumentException)
{
return null;
}
- return _callSiteCache[callSiteKey] = CreateConstructorCallSite(lifetime, serviceType, closedType, callSiteChain);
+ return _callSiteCache[callSiteKey] = CreateConstructorCallSite(lifetime, serviceIdentifier, closedType, callSiteChain);
}
return null;
private ConstructorCallSite CreateConstructorCallSite(
ResultCache lifetime,
- Type serviceType,
+ ServiceIdentifier serviceIdentifier,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType,
CallSiteChain callSiteChain)
{
try
{
- callSiteChain.Add(serviceType, implementationType);
+ callSiteChain.Add(serviceIdentifier, implementationType);
ConstructorInfo[] constructors = implementationType.GetConstructors();
ServiceCallSite[]? parameterCallSites = null;
ParameterInfo[] parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
- return new ConstructorCallSite(lifetime, serviceType, constructor);
+ return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor);
}
parameterCallSites = CreateArgumentCallSites(
+ serviceIdentifier,
implementationType,
callSiteChain,
parameters,
throwIfCallSiteNotFound: true)!;
- return new ConstructorCallSite(lifetime, serviceType, constructor, parameterCallSites);
+ return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, constructor, parameterCallSites);
}
Array.Sort(constructors,
ParameterInfo[] parameters = constructors[i].GetParameters();
ServiceCallSite[]? currentParameterCallSites = CreateArgumentCallSites(
+ serviceIdentifier,
implementationType,
callSiteChain,
parameters,
else
{
Debug.Assert(parameterCallSites != null);
- return new ConstructorCallSite(lifetime, serviceType, bestConstructor, parameterCallSites);
+ return new ConstructorCallSite(lifetime, serviceIdentifier.ServiceType, bestConstructor, parameterCallSites);
}
}
finally
{
- callSiteChain.Remove(serviceType);
+ callSiteChain.Remove(serviceIdentifier);
}
}
/// <returns>Not <b>null</b> if <b>throwIfCallSiteNotFound</b> is true</returns>
private ServiceCallSite[]? CreateArgumentCallSites(
+ ServiceIdentifier serviceIdentifier,
Type implementationType,
CallSiteChain callSiteChain,
ParameterInfo[] parameters,
bool throwIfCallSiteNotFound)
{
var parameterCallSites = new ServiceCallSite[parameters.Length];
+
for (int index = 0; index < parameters.Length; index++)
{
+ ServiceCallSite? callSite = null;
Type parameterType = parameters[index].ParameterType;
- ServiceCallSite? callSite = GetCallSite(parameterType, callSiteChain);
+ if (parameters[index].CustomAttributes != null)
+ {
+ foreach (var attribute in parameters[index].GetCustomAttributes(true))
+ {
+ if (serviceIdentifier.ServiceKey != null && attribute is ServiceKeyAttribute)
+ {
+ // Check if the parameter type matches
+ if (parameterType != serviceIdentifier.ServiceKey.GetType())
+ {
+ throw new InvalidOperationException(SR.InvalidServiceKeyType);
+ }
+ callSite = new ConstantCallSite(parameterType, serviceIdentifier.ServiceKey);
+ break;
+ }
+ if (attribute is FromKeyedServicesAttribute keyed)
+ {
+ var parameterSvcId = new ServiceIdentifier(keyed.Key, parameterType);
+ callSite = GetCallSite(parameterSvcId, callSiteChain);
+ break;
+ }
+ }
+ }
+
+ callSite ??= GetCallSite(ServiceIdentifier.FromServiceType(parameterType), callSiteChain);
if (callSite == null && ParameterDefaultValue.TryGetDefaultValue(parameters[index], out object? defaultValue))
{
}
}
- public void Add(Type type, ServiceCallSite serviceCallSite)
+ public void Add(ServiceIdentifier serviceIdentifier, ServiceCallSite serviceCallSite)
{
- _callSiteCache[new ServiceCacheKey(type, DefaultSlot)] = serviceCallSite;
+ _callSiteCache[new ServiceCacheKey(serviceIdentifier, DefaultSlot)] = serviceCallSite;
}
- public bool IsService(Type serviceType)
+ public bool IsService(Type serviceType) => IsService(new ServiceIdentifier(null, serviceType));
+
+ public bool IsKeyedService(Type serviceType, object? key) => IsService(new ServiceIdentifier(key, serviceType));
+
+ internal bool IsService(ServiceIdentifier serviceIdentifier)
{
+ var serviceType = serviceIdentifier.ServiceType;
+
if (serviceType is null)
{
throw new ArgumentNullException(nameof(serviceType));
return false;
}
- if (_descriptorLookup.ContainsKey(serviceType))
+ if (_descriptorLookup.ContainsKey(serviceIdentifier))
+ {
+ return true;
+ }
+
+ if (serviceIdentifier.ServiceKey != null && _descriptorLookup.ContainsKey(new ServiceIdentifier(KeyedService.AnyKey, serviceType)))
{
return true;
}
{
// We special case IEnumerable since it isn't explicitly registered in the container
// yet we can manifest instances of it when requested.
- return genericDefinition == typeof(IEnumerable<>) || _descriptorLookup.ContainsKey(genericDefinition);
+ return genericDefinition == typeof(IEnumerable<>) || _descriptorLookup.ContainsKey(serviceIdentifier.GetGenericTypeDefinition());
}
// These are the built in service types that aren't part of the list of service descriptors
// If you update these make sure to also update the code in ServiceProvider.ctor
return serviceType == typeof(IServiceProvider) ||
serviceType == typeof(IServiceScopeFactory) ||
- serviceType == typeof(IServiceProviderIsService);
+ serviceType == typeof(IServiceProviderIsService) ||
+ serviceType == typeof(IServiceProviderIsKeyedService);
+ }
+
+ /// <summary>
+ /// Returns true if both keys are null or equals, or if key1 is KeyedService.AnyKey and key2 is not null
+ /// </summary>
+ private static bool KeysMatch(object? key1, object? key2)
+ {
+ if (key1 == null && key2 == null)
+ return true;
+
+ if (key1 != null && key2 != null)
+ return key1.Equals(KeyedService.AnyKey) || key1.Equals(key2);
+
+ return false;
}
private struct ServiceDescriptorCacheItem
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
- internal sealed class CallSiteValidator: CallSiteVisitor<CallSiteValidator.CallSiteValidatorState, Type?>
+ internal sealed class CallSiteValidator : CallSiteVisitor<CallSiteValidator.CallSiteValidatorState, Type?>
{
// Keys are services being resolved via GetService, values - first scoped service in their call site tree
private readonly ConcurrentDictionary<ServiceCacheKey, Type> _scopedServices = new ConcurrentDictionary<ServiceCacheKey, Type>();
if (serviceType == scopedService)
{
throw new InvalidOperationException(
- SR.Format(SR.DirectScopedResolvedFromRootException, serviceType,
+ SR.Format(SR.DirectScopedResolvedFromRootException, callSite.ServiceType,
nameof(ServiceLifetime.Scoped).ToLowerInvariant()));
}
throw new InvalidOperationException(
SR.Format(SR.ScopedResolvedFromRootException,
- serviceType,
+ callSite.ServiceType,
scopedService,
nameof(ServiceLifetime.Scoped).ToLowerInvariant()));
}
ServiceType = serviceType;
}
+ public FactoryCallSite(ResultCache cache, Type serviceType, object serviceKey, Func<IServiceProvider, object, object> factory) : base(cache)
+ {
+ Factory = sp => factory(sp, serviceKey);
+ ServiceType = serviceType;
+ }
+
public override Type ServiceType { get; }
public override Type? ImplementationType => null;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Emit;
+using System.Runtime.InteropServices;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
private static void AddCacheKey(ILEmitResolverBuilderContext argument, ServiceCacheKey key)
{
- Debug.Assert(key.Type != null);
+ Debug.Assert(key.ServiceIdentifier != null);
+ var id = key.ServiceIdentifier.Value;
- // new ServiceCacheKey(typeof(key.Type), key.Slot)
- argument.Generator.Emit(OpCodes.Ldtoken, key.Type);
+ // new ServiceCacheKey(key.ServiceKey, key.type, key.slot)
+ AddConstant(argument, id.ServiceKey);
+ argument.Generator.Emit(OpCodes.Ldtoken, id.ServiceType);
argument.Generator.Emit(OpCodes.Call, GetTypeFromHandleMethod);
argument.Generator.Emit(OpCodes.Ldc_I4, key.Slot);
argument.Generator.Emit(OpCodes.Newobj, CacheKeyCtor);
{
public static ResultCache None(Type serviceType)
{
- var cacheKey = new ServiceCacheKey(serviceType, 0);
+ var cacheKey = new ServiceCacheKey(ServiceIdentifier.FromServiceType(serviceType), 0);
return new ResultCache(CallSiteResultCacheLocation.None, cacheKey);
}
Key = cacheKey;
}
- public ResultCache(ServiceLifetime lifetime, Type? type, int slot)
+ public ResultCache(ServiceLifetime lifetime, ServiceIdentifier? serviceIdentifier, int slot)
{
- Debug.Assert(lifetime == ServiceLifetime.Transient || type != null);
+ Debug.Assert(lifetime == ServiceLifetime.Transient || serviceIdentifier != null);
switch (lifetime)
{
Location = CallSiteResultCacheLocation.None;
break;
}
- Key = new ServiceCacheKey(type, slot);
+ Key = new ServiceCacheKey(serviceIdentifier, slot);
}
public CallSiteResultCacheLocation Location { get; set; }
/// <summary>
/// Type of service being cached
/// </summary>
- public Type? Type { get; }
+ public ServiceIdentifier? ServiceIdentifier { get; }
/// <summary>
/// Reverse index of the service when resolved in <c>IEnumerable<Type></c> where default instance gets slot 0.
/// </summary>
public int Slot { get; }
- public ServiceCacheKey(Type? type, int slot)
+ public ServiceCacheKey(object key, Type type, int slot)
{
- Type = type;
+ ServiceIdentifier = new ServiceIdentifier(key, type);
+ Slot = slot;
+ }
+
+ public ServiceCacheKey(ServiceIdentifier? type, int slot)
+ {
+ ServiceIdentifier = type;
Slot = slot;
}
/// <param name="other">An instance to compare with this instance.</param>
/// <returns>true if the current instance is equal to the other instance; otherwise, false.</returns>
public bool Equals(ServiceCacheKey other) =>
- Type == other.Type && Slot == other.Slot;
+ ServiceIdentifier.Equals(other.ServiceIdentifier) && Slot == other.Slot;
public override bool Equals([NotNullWhen(true)] object? obj) =>
obj is ServiceCacheKey other && Equals(other);
{
unchecked
{
- return ((Type?.GetHashCode() ?? 23) * 397) ^ Slot;
+ return ((ServiceIdentifier?.GetHashCode() ?? 23) * 397) ^ Slot;
}
}
}
public abstract CallSiteKind Kind { get; }
public ResultCache Cache { get; }
public object? Value { get; set; }
+ public object? Key { get; set; }
public bool CaptureDisposable =>
ImplementationType == null ||
--- /dev/null
+// 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.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
+{
+ internal static class ServiceDescriptorExtensions
+ {
+ public static bool HasImplementationInstance(this ServiceDescriptor serviceDescriptor) => GetImplementationInstance(serviceDescriptor) != null;
+
+ public static bool HasImplementationFactory(this ServiceDescriptor serviceDescriptor) => GetImplementationFactory(serviceDescriptor) != null;
+
+ public static bool HasImplementationType(this ServiceDescriptor serviceDescriptor) => GetImplementationType(serviceDescriptor) != null;
+
+ public static object? GetImplementationInstance(this ServiceDescriptor serviceDescriptor)
+ {
+ return serviceDescriptor.IsKeyedService
+ ? serviceDescriptor.KeyedImplementationInstance
+ : serviceDescriptor.ImplementationInstance;
+ }
+
+ public static object? GetImplementationFactory(this ServiceDescriptor serviceDescriptor)
+ {
+ return serviceDescriptor.IsKeyedService
+ ? serviceDescriptor.KeyedImplementationFactory
+ : serviceDescriptor.ImplementationFactory;
+ }
+
+ [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+ public static Type? GetImplementationType(this ServiceDescriptor serviceDescriptor)
+ {
+ return serviceDescriptor.IsKeyedService
+ ? serviceDescriptor.KeyedImplementationType
+ : serviceDescriptor.ImplementationType;
+ }
+ }
+}
--- /dev/null
+// 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.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
+{
+ internal readonly struct ServiceIdentifier : IEquatable<ServiceIdentifier>
+ {
+ public object? ServiceKey { get; }
+
+ public Type ServiceType { get; }
+
+ public ServiceIdentifier(Type serviceType)
+ {
+ ServiceType = serviceType;
+ }
+
+ public ServiceIdentifier(object? serviceKey, Type serviceType)
+ {
+ ServiceKey = serviceKey;
+ ServiceType = serviceType;
+ }
+
+ public static ServiceIdentifier FromDescriptor(ServiceDescriptor serviceDescriptor)
+ => new ServiceIdentifier(serviceDescriptor.ServiceKey, serviceDescriptor.ServiceType);
+
+ public static ServiceIdentifier FromServiceType(Type type) => new ServiceIdentifier(null, type);
+
+ public bool Equals(ServiceIdentifier other)
+ {
+ if (ServiceKey == null && other.ServiceKey == null)
+ {
+ return ServiceType.Equals(other.ServiceType);
+ }
+ else if (ServiceKey != null && other.ServiceKey != null)
+ {
+ return ServiceType.Equals(other.ServiceType) && ServiceKey.Equals(other.ServiceKey);
+ }
+ return false;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ return obj is ServiceIdentifier && Equals((ServiceIdentifier)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ if (ServiceKey == null)
+ {
+ return ServiceType.GetHashCode();
+ }
+ unchecked
+ {
+ return ((ServiceType?.GetHashCode() ?? 23) * 397) ^ ServiceKey.GetHashCode();
+ }
+ }
+
+ public bool IsConstructedGenericType => ServiceType.IsConstructedGenericType;
+
+ public ServiceIdentifier GetGenericTypeDefinition() => new ServiceIdentifier(ServiceKey, ServiceType.GetGenericTypeDefinition());
+
+ public override string? ToString()
+ {
+ if (ServiceKey == null)
+ {
+ return ServiceType.ToString();
+ }
+
+ return $"({ServiceKey}, {ServiceType})";
+ }
+ }
+}
ThrowHelper.ThrowObjectDisposedException();
}
- return RootProvider.GetService(serviceType, this);
+ return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}
public IServiceProvider ServiceProvider => this;
/// </summary>
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(ServiceProviderDebugView))]
- public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDisposable
+ public sealed class ServiceProvider : IServiceProvider, IKeyedServiceProvider, IDisposable, IAsyncDisposable
{
private readonly CallSiteValidator? _callSiteValidator;
- private readonly Func<Type, Func<ServiceProviderEngineScope, object?>> _createServiceAccessor;
+ private readonly Func<ServiceIdentifier, Func<ServiceProviderEngineScope, object?>> _createServiceAccessor;
// Internal for testing
internal ServiceProviderEngine _engine;
private bool _disposed;
- private readonly ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object?>> _realizedServices;
+ private readonly ConcurrentDictionary<ServiceIdentifier, Func<ServiceProviderEngineScope, object?>> _realizedServices;
internal CallSiteFactory CallSiteFactory { get; }
Root = new ServiceProviderEngineScope(this, isRootScope: true);
_engine = GetEngine();
_createServiceAccessor = CreateServiceAccessor;
- _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object?>>();
+ _realizedServices = new ConcurrentDictionary<ServiceIdentifier, Func<ServiceProviderEngineScope, object?>>();
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
// The list of built in services that aren't part of the list of service descriptors
// keep this in sync with CallSiteFactory.IsService
- CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
- CallSiteFactory.Add(typeof(IServiceScopeFactory), new ConstantCallSite(typeof(IServiceScopeFactory), Root));
- CallSiteFactory.Add(typeof(IServiceProviderIsService), new ConstantCallSite(typeof(IServiceProviderIsService), CallSiteFactory));
+ CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProvider)), new ServiceProviderCallSite());
+ CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceScopeFactory)), new ConstantCallSite(typeof(IServiceScopeFactory), Root));
+ CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProviderIsService)), new ConstantCallSite(typeof(IServiceProviderIsService), CallSiteFactory));
+ CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProviderIsKeyedService)), new ConstantCallSite(typeof(IServiceProviderIsKeyedService), CallSiteFactory));
if (options.ValidateScopes)
{
/// </summary>
/// <param name="serviceType">The type of the service to get.</param>
/// <returns>The service that was produced.</returns>
- public object? GetService(Type serviceType) => GetService(serviceType, Root);
+ public object? GetService(Type serviceType) => GetService(ServiceIdentifier.FromServiceType(serviceType), Root);
+
+ public object? GetKeyedService(Type serviceType, object? serviceKey)
+ => GetService(new ServiceIdentifier(serviceKey, serviceType), Root);
+
+ public object GetRequiredKeyedService(Type serviceType, object? serviceKey)
+ {
+ object? service = GetKeyedService(serviceType, serviceKey);
+ if (service == null)
+ {
+ throw new InvalidOperationException(SR.Format(SR.NoServiceRegistered, serviceType));
+ }
+ return service;
+ }
internal bool IsDisposed() => _disposed;
_callSiteValidator?.ValidateResolution(callSite, scope, Root);
}
- internal object? GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
+ internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
- Func<ServiceProviderEngineScope, object?> realizedService = _realizedServices.GetOrAdd(serviceType, _createServiceAccessor);
+ Func<ServiceProviderEngineScope, object?> realizedService = _realizedServices.GetOrAdd(serviceIdentifier, _createServiceAccessor);
var result = realizedService.Invoke(serviceProviderEngineScope);
- System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceType));
+ System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));
return result;
}
}
}
- private Func<ServiceProviderEngineScope, object?> CreateServiceAccessor(Type serviceType)
+ private Func<ServiceProviderEngineScope, object?> CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
- ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
+ ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceIdentifier, new CallSiteChain());
if (callSite != null)
{
- DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceType, callSite);
+ DependencyInjectionEventSource.Log.CallSiteBuilt(this, serviceIdentifier.ServiceType, callSite);
OnCreate(callSite);
// Optimize singleton case
object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
return scope =>
{
- DependencyInjectionEventSource.Log.ServiceResolved(this, serviceType);
+ DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
return value;
};
}
return scope =>
{
OnResolve(callSite, scope);
- DependencyInjectionEventSource.Log.ServiceResolved(this, serviceType);
+ DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
return realizedService(scope);
};
}
internal void ReplaceServiceAccessor(ServiceCallSite callSite, Func<ServiceProviderEngineScope, object?> accessor)
{
- _realizedServices[callSite.ServiceType] = accessor;
+ _realizedServices[new ServiceIdentifier(callSite.Key, callSite.ServiceType)] = accessor;
}
internal IServiceScope CreateScope()
namespace Microsoft.Extensions.DependencyInjection.Tests
{
+ internal static class CallSiteTestsExtensions
+ {
+ internal static ServiceCallSite GetCallSite(this CallSiteFactory callSiteFactory, Type type, CallSiteChain callSiteChain)
+ {
+ return callSiteFactory.GetCallSite(ServiceIdentifier.FromServiceType(type), callSiteChain);
+ }
+ }
+
public class CallSiteTests
{
public static IEnumerable<object[]> TestServiceDescriptors(ServiceLifetime lifetime)
var disposables = new List<object>();
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
-
+
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var disposables = new List<object>();
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
-
+
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
--- /dev/null
+// 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.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection.Specification;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection.Tests
+{
+ public class KeyedServiceProviderDefaultContainerTests : KeyedDependencyInjectionSpecificationTests
+ {
+ protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) => collection.BuildServiceProvider(ServiceProviderMode.Default);
+ }
+
+ public class KeyedServiceProviderDynamicContainerTests : KeyedDependencyInjectionSpecificationTests
+ {
+ protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) => collection.BuildServiceProvider();
+ }
+
+ public class KeyedServiceProviderExpressionContainerTests : KeyedDependencyInjectionSpecificationTests
+ {
+ protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) => collection.BuildServiceProvider(ServiceProviderMode.Expressions);
+ }
+
+ public class KeyedServiceProviderILEmitContainerTests : KeyedDependencyInjectionSpecificationTests
+ {
+ protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) => collection.BuildServiceProvider(ServiceProviderMode.ILEmit);
+ }
+}
--- /dev/null
+// 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 Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
+using Microsoft.Extensions.DependencyInjection.Tests;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public class ServiceCollectionKeyedServiceExtensionsTest
+ {
+ private static readonly FakeService _instance = new FakeService();
+
+ public static TheoryData AddImplementationTypeData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ var implementationType = typeof(FakeService);
+ return new TheoryData<Action<IServiceCollection>, Type, object, Type, ServiceLifetime>
+ {
+ { collection => collection.AddKeyedTransient(serviceType, "some-key-1", implementationType), serviceType, "some-key-1", implementationType, ServiceLifetime.Transient },
+ { collection => collection.AddKeyedTransient<IFakeService, FakeService>("some-key-2"), serviceType, "some-key-2", implementationType, ServiceLifetime.Transient },
+ { collection => collection.AddKeyedTransient<IFakeService>("some-key-3"), serviceType, "some-key-3", serviceType, ServiceLifetime.Transient },
+ { collection => collection.AddKeyedTransient(implementationType, "some-key-4"), implementationType, "some-key-4", implementationType, ServiceLifetime.Transient },
+
+ { collection => collection.AddKeyedScoped(serviceType, "some-key-5", implementationType), serviceType, "some-key-5", implementationType, ServiceLifetime.Scoped },
+ { collection => collection.AddKeyedScoped<IFakeService, FakeService>("some-key-6"), serviceType, "some-key-6", implementationType, ServiceLifetime.Scoped },
+ { collection => collection.AddKeyedScoped<IFakeService>("some-key-7"), serviceType, "some-key-7", serviceType, ServiceLifetime.Scoped },
+ { collection => collection.AddKeyedScoped(implementationType, "some-key-8"), implementationType, "some-key-8", implementationType, ServiceLifetime.Scoped },
+
+ { collection => collection.AddKeyedSingleton(serviceType, "some-key-9", implementationType), serviceType, "some-key-9", implementationType, ServiceLifetime.Singleton },
+ { collection => collection.AddKeyedSingleton<IFakeService, FakeService>("some-key-10"), serviceType, "some-key-10", implementationType, ServiceLifetime.Singleton },
+ { collection => collection.AddKeyedSingleton<IFakeService>("some-key-12"), serviceType, "some-key-12", serviceType, ServiceLifetime.Singleton },
+ { collection => collection.AddKeyedSingleton(serviceType: implementationType, "some-key-13"), implementationType, "some-key-13", implementationType, ServiceLifetime.Singleton },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(AddImplementationTypeData))]
+ public void AddWithTypeAddsServiceWithRightLifecyle(Action<IServiceCollection> addTypeAction,
+ Type expectedServiceType,
+ object expectedKey,
+ Type expectedImplementationType,
+ ServiceLifetime lifeCycle)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ addTypeAction(collection);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(expectedServiceType, descriptor.ServiceType);
+ Assert.Equal(expectedKey, descriptor.ServiceKey);
+ Assert.Equal(expectedImplementationType, descriptor.KeyedImplementationType);
+ Assert.Equal(lifeCycle, descriptor.Lifetime);
+ }
+
+ public static TheoryData AddImplementationFactoryData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ var implementationType = typeof(FakeService);
+ var objectType = typeof(object);
+
+ return new TheoryData<Action<IServiceCollection>, Type, object, Type, ServiceLifetime>
+ {
+ { collection => collection.AddKeyedTransient(serviceType, "some-key-1", (s,k) => new FakeService()), serviceType, "some-key-1", objectType, ServiceLifetime.Transient },
+ { collection => collection.AddKeyedTransient<IFakeService>("some-key-2", (s,k) => new FakeService()), serviceType, "some-key-2", serviceType, ServiceLifetime.Transient },
+ { collection => collection.AddKeyedTransient<IFakeService, FakeService>("some-key-3", (s,k) => new FakeService()), serviceType, "some-key-3", implementationType, ServiceLifetime.Transient },
+
+ { collection => collection.AddKeyedScoped(serviceType, "some-key-4", (s,k) => new FakeService()), serviceType, "some-key-4", objectType, ServiceLifetime.Scoped },
+ { collection => collection.AddKeyedScoped<IFakeService>("some-key-5", (s,k) => new FakeService()), serviceType, "some-key-5", serviceType, ServiceLifetime.Scoped },
+ { collection => collection.AddKeyedScoped<IFakeService, FakeService>("some-key-6", (s,k) => new FakeService()), serviceType, "some-key-6", implementationType, ServiceLifetime.Scoped },
+
+ { collection => collection.AddKeyedSingleton(serviceType, "some-key-7", (s,k) => new FakeService()), serviceType, "some-key-7", objectType, ServiceLifetime.Singleton },
+ { collection => collection.AddKeyedSingleton<IFakeService>("some-key-8", (s,k) => new FakeService()), serviceType, "some-key-8", serviceType, ServiceLifetime.Singleton },
+ { collection => collection.AddKeyedSingleton<IFakeService, FakeService>("some-key-9", (s,k) => new FakeService()), serviceType, "some-key-9", implementationType, ServiceLifetime.Singleton },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(AddImplementationFactoryData))]
+ public void AddWithFactoryAddsServiceWithRightLifecyle(
+ Action<IServiceCollection> addAction,
+ Type serviceType,
+ object? serviceKey,
+ Type implementationType,
+ ServiceLifetime lifeCycle)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ addAction(collection);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(serviceType, descriptor.ServiceType);
+ Assert.Equal(serviceKey, descriptor.ServiceKey);
+ Assert.Equal(implementationType, descriptor.GetImplementationType());
+ Assert.Equal(lifeCycle, descriptor.Lifetime);
+ }
+
+ public static TheoryData AddSingletonData
+ {
+ get
+ {
+ return new TheoryData<Action<IServiceCollection>>
+ {
+ { collection => collection.AddKeyedSingleton<IFakeService>("service", _instance) },
+ { collection => collection.AddKeyedSingleton(typeof(IFakeService), "service", _instance) },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(AddSingletonData))]
+ public void AddSingleton_AddsWithSingletonLifecycle(Action<IServiceCollection> addAction)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ addAction(collection);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(typeof(IFakeService), descriptor.ServiceType);
+ Assert.Equal("service", descriptor.ServiceKey.ToString());
+ Assert.Same(_instance, descriptor.KeyedImplementationInstance);
+ Assert.Equal(ServiceLifetime.Singleton, descriptor.Lifetime);
+ }
+
+ [Theory]
+ [MemberData(nameof(AddSingletonData))]
+ public void TryAddNoOpFailsIfExists(Action<IServiceCollection> addAction)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ addAction(collection);
+ var d = ServiceDescriptor.KeyedTransient<IFakeService, FakeService>("service");
+
+ // Act
+ collection.TryAdd(d);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(typeof(IFakeService), descriptor.ServiceType);
+ Assert.Same(_instance, descriptor.KeyedImplementationInstance);
+ Assert.Equal(ServiceLifetime.Singleton, descriptor.Lifetime);
+ }
+
+ public static TheoryData TryAddImplementationTypeData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ var implementationType = typeof(FakeService);
+ return new TheoryData<Action<IServiceCollection>, Type, object, Type, ServiceLifetime>
+ {
+ { collection => collection.TryAddKeyedTransient(serviceType, "key-1", implementationType), serviceType, "key-1", implementationType, ServiceLifetime.Transient },
+ { collection => collection.TryAddKeyedTransient<IFakeService, FakeService>("key-2"), serviceType, "key-2", implementationType, ServiceLifetime.Transient },
+ { collection => collection.TryAddKeyedTransient<IFakeService>("key-3"), serviceType, "key-3", serviceType, ServiceLifetime.Transient },
+ { collection => collection.TryAddKeyedTransient(implementationType, "key-4"), implementationType, "key-4", implementationType, ServiceLifetime.Transient },
+
+ { collection => collection.TryAddKeyedScoped(serviceType, "key-1", implementationType), serviceType, "key-1", implementationType, ServiceLifetime.Scoped },
+ { collection => collection.TryAddKeyedScoped<IFakeService, FakeService>("key-2"), serviceType, "key-2", implementationType, ServiceLifetime.Scoped },
+ { collection => collection.TryAddKeyedScoped<IFakeService>("key-3"), serviceType, "key-3", serviceType, ServiceLifetime.Scoped },
+ { collection => collection.TryAddKeyedScoped(implementationType, "key-4"), implementationType, "key-4", implementationType, ServiceLifetime.Scoped },
+
+ { collection => collection.TryAddKeyedSingleton(serviceType, "key-5", implementationType), serviceType, "key-5", implementationType, ServiceLifetime.Singleton },
+ { collection => collection.TryAddKeyedSingleton<IFakeService, FakeService>("key-6"), serviceType, "key-6", implementationType, ServiceLifetime.Singleton },
+ { collection => collection.TryAddKeyedSingleton<IFakeService>("key-7"), serviceType, "key-7", serviceType, ServiceLifetime.Singleton },
+ { collection => collection.TryAddKeyedSingleton(service: implementationType, "key-8"), implementationType, "key-8", implementationType, ServiceLifetime.Singleton },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TryAddImplementationTypeData))]
+ public void TryAdd_WithType_AddsService(
+ Action<IServiceCollection> addAction,
+ Type expectedServiceType,
+ object expectedServiceKey,
+ Type expectedImplementationType,
+ ServiceLifetime expectedLifetime)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ addAction(collection);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(expectedServiceType, descriptor.ServiceType);
+ Assert.Equal(expectedServiceKey, descriptor.ServiceKey);
+ Assert.Same(expectedImplementationType, descriptor.KeyedImplementationType);
+ Assert.Equal(expectedLifetime, descriptor.Lifetime);
+ }
+
+ [Theory]
+ [MemberData(nameof(TryAddImplementationTypeData))]
+ public void TryAdd_WithType_DoesNotAddDuplicate(
+ Action<IServiceCollection> addAction,
+ Type expectedServiceType,
+ object expectedServiceKey,
+ // Test verifies that descriptor is not added so we don't need to assert it's properties
+#pragma warning disable xUnit1026
+ Type expectedImplementationType,
+ ServiceLifetime expectedLifetime
+#pragma warning restore xUnit1026
+ )
+ {
+ // Arrange
+ var collection = new ServiceCollection
+ {
+ ServiceDescriptor.KeyedTransient(expectedServiceType, expectedServiceKey, expectedServiceType)
+ };
+
+ // Act
+ addAction(collection);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(expectedServiceType, descriptor.ServiceType);
+ Assert.Same(expectedServiceType, descriptor.KeyedImplementationType);
+ Assert.Equal(ServiceLifetime.Transient, descriptor.Lifetime);
+ }
+
+ [Fact]
+ public void TryAddIfMissingActuallyAdds()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ var key = new object();
+ var d = ServiceDescriptor.KeyedTransient<IFakeService, FakeService>(key);
+
+ // Act
+ collection.TryAdd(d);
+
+ // Assert
+ var descriptor = Assert.Single(collection);
+ Assert.Equal(typeof(IFakeService), descriptor.ServiceType);
+ Assert.Equal(key, descriptor.ServiceKey);
+ Assert.Null(descriptor.KeyedImplementationInstance);
+ Assert.Equal(ServiceLifetime.Transient, descriptor.Lifetime);
+ }
+
+ public static TheoryData TryAddEnumerableImplementationTypeData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ var implementationType = typeof(FakeService);
+ return new TheoryData<ServiceDescriptor, Type, object, Type, ServiceLifetime>
+ {
+ { ServiceDescriptor.KeyedTransient<IFakeService, FakeService>("service1"), serviceType, "service1", implementationType, ServiceLifetime.Transient },
+ { ServiceDescriptor.KeyedTransient<IFakeService, FakeService>("service2", (s,k) => new FakeService()), serviceType, "service2", implementationType, ServiceLifetime.Transient },
+
+ { ServiceDescriptor.KeyedScoped<IFakeService, FakeService>("service3"), serviceType, "service3", implementationType, ServiceLifetime.Scoped },
+ { ServiceDescriptor.KeyedScoped<IFakeService, FakeService>("service4", (s,k) => new FakeService()), serviceType, "service4", implementationType, ServiceLifetime.Scoped },
+
+ { ServiceDescriptor.KeyedSingleton<IFakeService, FakeService>("service5"), serviceType, "service5", implementationType, ServiceLifetime.Singleton },
+ { ServiceDescriptor.KeyedSingleton<IFakeService, FakeService>("service6", (s,k) => new FakeService()), serviceType, "service6", implementationType, ServiceLifetime.Singleton },
+
+ { ServiceDescriptor.KeyedSingleton<IFakeService>("service6", _instance), serviceType, "service6", implementationType, ServiceLifetime.Singleton },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TryAddEnumerableImplementationTypeData))]
+ public void TryAddEnumerable_AddsService(
+ ServiceDescriptor descriptor,
+ Type expectedServiceType,
+ object expectedKey,
+ Type expectedImplementationType,
+ ServiceLifetime expectedLifetime)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ // Act
+ collection.TryAddEnumerable(descriptor);
+
+ // Assert
+ var d = Assert.Single(collection);
+ Assert.Equal(expectedServiceType, d.ServiceType);
+ Assert.Equal(expectedKey, d.ServiceKey);
+ Assert.Equal(expectedImplementationType, d.GetImplementationType());
+ Assert.Equal(expectedLifetime, d.Lifetime);
+ }
+
+
+ [Theory]
+ [MemberData(nameof(TryAddEnumerableImplementationTypeData))]
+ public void TryAddEnumerable_DoesNotAddDuplicate(
+ ServiceDescriptor descriptor,
+ Type expectedServiceType,
+ object expectedKey,
+ Type expectedImplementationType,
+ ServiceLifetime expectedLifetime)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ collection.TryAddEnumerable(descriptor);
+
+ // Act
+ collection.TryAddEnumerable(descriptor);
+
+ // Assert
+ var d = Assert.Single(collection);
+ Assert.Equal(expectedServiceType, d.ServiceType);
+ Assert.Equal(expectedKey, d.ServiceKey);
+ Assert.Equal(expectedImplementationType, d.GetImplementationType());
+ Assert.Equal(expectedLifetime, d.Lifetime);
+ }
+
+ public static TheoryData TryAddEnumerableInvalidImplementationTypeData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ var key = new object();
+ var implementationType = typeof(FakeService);
+ var objectType = typeof(object);
+
+ return new TheoryData<ServiceDescriptor, Type, Type>
+ {
+ { ServiceDescriptor.KeyedTransient<IFakeService>(key, (s,k) => new FakeService()), serviceType, serviceType },
+ { ServiceDescriptor.KeyedTransient(serviceType, key, (s,k) => new FakeService()), serviceType, objectType },
+
+ { ServiceDescriptor.KeyedScoped<IFakeService>(key, (s,k) => new FakeService()), serviceType, serviceType },
+ { ServiceDescriptor.KeyedScoped(serviceType, key, (s,k) => new FakeService()), serviceType, objectType },
+
+ { ServiceDescriptor.KeyedSingleton<IFakeService>(key, (s,k) => new FakeService()), serviceType, serviceType },
+ { ServiceDescriptor.KeyedSingleton(serviceType, key, (s,k) => new FakeService()), serviceType, objectType },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TryAddEnumerableInvalidImplementationTypeData))]
+ public void TryAddEnumerable_ThrowsWhenAddingIndistinguishableImplementationType(
+ ServiceDescriptor descriptor,
+ Type serviceType,
+ Type implementationType)
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+
+ AssertExtensions.ThrowsContains<ArgumentException>(() => collection.TryAddEnumerable(descriptor),
+ string.Format(@"Implementation type cannot be '{0}' because it is indistinguishable from other services registered for '{1}'.", implementationType, serviceType));
+ }
+
+ [Fact]
+ public void AddSequence_AddsServicesToCollection()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ var descriptor1 = new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient);
+ var descriptor2 = new ServiceDescriptor(typeof(IFakeOuterService), "key1", typeof(FakeOuterService), ServiceLifetime.Transient);
+ var descriptors = new[] { descriptor1, descriptor2 };
+
+ // Act
+ var result = collection.Add(descriptors);
+
+ // Assert
+ Assert.Equal(descriptors, collection);
+ }
+
+ [Fact]
+ public void Replace_AddsServiceIfServiceTypeIsNotRegistered()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ var descriptor1 = new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient);
+ var descriptor2 = new ServiceDescriptor(typeof(IFakeOuterService), "key1", typeof(FakeOuterService), ServiceLifetime.Transient);
+ collection.Add(descriptor1);
+
+ // Act
+ collection.Replace(descriptor2);
+
+ // Assert
+ Assert.Equal(new[] { descriptor1, descriptor2 }, collection);
+ }
+
+ [Fact]
+ public void Replace_ReplacesFirstServiceWithMatchingServiceType()
+ {
+ // Arrange
+ var collection = new ServiceCollection();
+ var descriptor1 = new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient);
+ var descriptor2 = new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient);
+ collection.Add(descriptor1);
+ collection.Add(descriptor2);
+ var descriptor3 = new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Singleton);
+
+ // Act
+ collection.Replace(descriptor3);
+
+ // Assert
+ Assert.Equal(new[] { descriptor2, descriptor3 }, collection);
+ }
+
+ [Fact]
+ public void RemoveAll_RemovesAllServicesWithMatchingServiceType()
+ {
+ // Arrange
+ var descriptor = new ServiceDescriptor(typeof(IFakeServiceInstance), "key1", typeof(FakeService), ServiceLifetime.Transient);
+ var collection = new ServiceCollection
+ {
+ descriptor,
+ new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient),
+ new ServiceDescriptor(typeof(IFakeService), "key1", typeof(FakeService), ServiceLifetime.Transient)
+ };
+
+ // Act
+ collection.RemoveAllKeyed<IFakeService>("key1");
+
+ // Assert
+ Assert.Equal(new[] { descriptor }, collection);
+ }
+
+ public static TheoryData NullServiceKeyData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ object key = null;
+ var implementationType = typeof(FakeService);
+ var objectType = typeof(object);
+
+ return new TheoryData<ServiceDescriptor>
+ {
+ { ServiceDescriptor.KeyedTransient<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedTransient<IFakeService>(key, (sp, key) => new FakeService()) },
+ { ServiceDescriptor.KeyedScoped<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedScoped<IFakeService>(key, (sp, key) => new FakeService()) },
+ { ServiceDescriptor.KeyedSingleton<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedSingleton<IFakeService>(key, new FakeService()) },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(NullServiceKeyData))]
+ public void NullServiceKey_IsKeyedServiceFalse(ServiceDescriptor serviceDescriptor)
+ {
+ Assert.False(serviceDescriptor.IsKeyedService);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.KeyedImplementationInstance);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.KeyedImplementationType);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.KeyedImplementationFactory);
+ }
+
+ public static TheoryData NotNullServiceKeyData
+ {
+ get
+ {
+ var serviceType = typeof(IFakeService);
+ object key = new();
+ var implementationType = typeof(FakeService);
+ var objectType = typeof(object);
+
+ return new TheoryData<ServiceDescriptor>
+ {
+ { ServiceDescriptor.KeyedTransient<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedTransient<IFakeService>(key, (sp, key) => new FakeService()) },
+ { ServiceDescriptor.KeyedScoped<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedScoped<IFakeService>(key, (sp, key) => new FakeService()) },
+ { ServiceDescriptor.KeyedSingleton<IFakeService, FakeService>(key) },
+ { ServiceDescriptor.KeyedSingleton<IFakeService>(key, new FakeService()) },
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(NotNullServiceKeyData))]
+ public void NotNullServiceKey_IsKeyedServiceTrue(ServiceDescriptor serviceDescriptor)
+ {
+ Assert.True(serviceDescriptor.IsKeyedService);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.ImplementationInstance);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.ImplementationType);
+ Assert.Throws<InvalidOperationException>(() => serviceDescriptor.ImplementationFactory);
+ }
+ }
+}
Assert.Equal(expectedLocation, callSite.Cache.Location);
Assert.Equal(0, callSite.Cache.Key.Slot);
- Assert.Equal(typeof(IEnumerable<FakeService>), callSite.Cache.Key.Type);
+ Assert.Equal(typeof(IEnumerable<FakeService>), callSite.Cache.Key.ServiceIdentifier.Value.ServiceType);
}
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))]
var callSiteFactory = new CallSiteFactory(collection.ToArray());
- return type => callSiteFactory.GetCallSite(type, new CallSiteChain());
+ return type => callSiteFactory.GetCallSite(ServiceIdentifier.FromServiceType(type), new CallSiteChain());
}
private static IEnumerable<Type> GetParameters(ConstructorCallSite constructorCallSite) =>
{
var provider = new ServiceProvider(new ServiceCollection(), ServiceProviderOptions.Default);
var serviceProviderEngineScope = new ServiceProviderEngineScope(provider, isRootScope: true);
- serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null);
+ serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(ServiceIdentifier.FromServiceType(typeof(IFakeService)), 0), null);
serviceProviderEngineScope.Dispose();
serviceProviderEngineScope.Dispose();
}