| File: EmptyServiceProvider.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.DependencyInjection.Abstractions\src\Microsoft.Extensions.DependencyInjection.Abstractions.csproj (Microsoft.Extensions.DependencyInjection.Abstractions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Microsoft.Extensions.DependencyInjection { /// <summary> /// Provides an <see cref="IServiceProvider"/> that contains no application services. /// </summary> /// <remarks> /// <para> /// Like the <see cref="IServiceProvider"/> returned by <c>new ServiceCollection().BuildServiceProvider()</c>, /// this provider still exposes the built-in services (<see cref="IServiceProvider"/>, /// <see cref="IServiceScopeFactory"/>, <see cref="IServiceProviderIsService"/> and /// <see cref="IServiceProviderIsKeyedService"/>) and resolves requests for /// <see cref="IEnumerable{T}"/> to an empty sequence. /// </para> /// <para> /// Use <see cref="Instance"/> to access the shared singleton instead of writing a custom empty provider. /// </para> /// </remarks> public sealed class EmptyServiceProvider : IKeyedServiceProvider, IServiceProviderIsKeyedService { private EmptyServiceProvider() { } /// <summary> /// Gets the shared <see cref="EmptyServiceProvider"/> instance. /// </summary> public static EmptyServiceProvider Instance { get; } = new EmptyServiceProvider(); /// <inheritdoc /> object? IServiceProvider.GetService(Type serviceType) => GetService(serviceType); /// <inheritdoc /> object? IKeyedServiceProvider.GetKeyedService(Type serviceType, object? serviceKey) => GetKeyedService(serviceType, serviceKey); /// <inheritdoc /> object IKeyedServiceProvider.GetRequiredKeyedService(Type serviceType, object? serviceKey) { object? service = GetKeyedService(serviceType, serviceKey); if (service is null) { ThrowNoServiceRegistered(serviceType, serviceKey); } return service; } /// <inheritdoc /> bool IServiceProviderIsService.IsService(Type serviceType) { ArgumentNullException.ThrowIfNull(serviceType); return !serviceType.IsGenericTypeDefinition && (IsEnumerable(serviceType) || IsBuiltInService(serviceType)); } /// <inheritdoc /> bool IServiceProviderIsKeyedService.IsKeyedService(Type serviceType, object? serviceKey) { ArgumentNullException.ThrowIfNull(serviceType); if (serviceType.IsGenericTypeDefinition) { return false; } return IsEnumerable(serviceType) || (serviceKey is null && IsBuiltInService(serviceType)); } private object? GetService(Type serviceType) { ArgumentNullException.ThrowIfNull(serviceType); if (serviceType == typeof(IServiceProvider) || serviceType == typeof(IServiceProviderIsService) || serviceType == typeof(IServiceProviderIsKeyedService)) { return this; } if (serviceType == typeof(IServiceScopeFactory)) { return EmptyServiceScope.Instance; } return IsEnumerable(serviceType) ? CreateEmptyEnumerable(serviceType) : null; } private object? GetKeyedService(Type serviceType, object? serviceKey) { ArgumentNullException.ThrowIfNull(serviceType); if (ReferenceEquals(serviceKey, KeyedService.AnyKey) && (!serviceType.IsGenericType || serviceType.GetGenericTypeDefinition() != typeof(IEnumerable<>))) { throw new InvalidOperationException(SR.Format(SR.KeyedServiceAnyKeyUsedToResolveService, nameof(IServiceProvider), nameof(IServiceScopeFactory))); } if (serviceKey is null) { return GetService(serviceType); } return IsEnumerable(serviceType) ? CreateEmptyEnumerable(serviceType) : null; } private static bool IsBuiltInService(Type serviceType) => serviceType == typeof(IServiceProvider) || serviceType == typeof(IServiceScopeFactory) || serviceType == typeof(IServiceProviderIsService) || serviceType == typeof(IServiceProviderIsKeyedService); private static bool IsEnumerable(Type serviceType) => serviceType.IsConstructedGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>); // Wraps RuntimeFeature.IsDynamicCodeSupported across target frameworks. private static bool IsDynamicCodeSupported => #if NETFRAMEWORK || NETSTANDARD2_0 true; #else RuntimeFeature.IsDynamicCodeSupported; #endif [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "The element type is guaranteed not to be a ValueType when dynamic code isn't supported.")] private static Array CreateEmptyEnumerable(Type serviceType) { Type itemType = serviceType.GenericTypeArguments[0]; if (!IsDynamicCodeSupported && itemType.IsValueType) { // Native AOT apps are not able to make an Enumerable of a ValueType service // since there is no guarantee the ValueType[] code has been generated. throw new InvalidOperationException(SR.Format(SR.AotCannotCreateEnumerableValueType, itemType)); } return Array.CreateInstance(itemType, 0); } [DoesNotReturn] private static void ThrowNoServiceRegistered(Type serviceType, object? serviceKey) { throw serviceKey is null ? new InvalidOperationException(SR.Format(SR.NoServiceRegistered, serviceType)) : new InvalidOperationException(SR.Format(SR.NoKeyedServiceRegistered, serviceType, serviceKey.GetType())); } private sealed class EmptyServiceScope : IServiceScopeFactory, IServiceScope { public static EmptyServiceScope Instance { get; } = new EmptyServiceScope(); public IServiceProvider ServiceProvider => EmptyServiceProvider.Instance; public IServiceScope CreateScope() => this; public void Dispose() { } } } }