IServiceProvider.GetServices<T>() 是否始终返回注册顺序中的可用服务实现?

Enr*_*one 5 c# dependency-injection .net-core asp.net-core asp.net-core-3.1

这个问题具体指的是ASP.NET core 3.1和内置的依赖注入容器(Microsoft DI)。

Microsoft 文档此 stackoverflow 问题IEnumerable<TService>确认,当为同一服务类型注册多个实现类型时,Microsoft DI 容器始终通过遵守注册顺序进行解析。订单是有保证的,并且有明确的记录。

有谁知道该方法是否同样适用IServiceProvider.GetServices<T>()

如果上述问题的答案是肯定的,那么即使在以下示例中(同一类的两个不同实例注册为同一服务类型的实现),这也成立吗?

public interface IService {}

public sealed class Foo : IService {}

var foo1 = new Foo();
var foo2 = new Foo();

services.AddSingleton<IService>(foo1);
services.AddSingleton<IService>(foo2);

var implementations = serviceProvider.GetServices<IFoo>();

// is it guaranteed that implementations[0] == foo1 and implementations[1] == foo2 ???
Run Code Online (Sandbox Code Playgroud)

Nko*_*osi 7

简短的答案是肯定的,因为内部GetServices* 扩展方法的解析与具有注入依赖项的IEnumerable<T>构造函数中的解析相同IEnumerable<T>

/// <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>
/// <returns>An enumeration of services of type <typeparamref name="T"/>.</returns>
public static IEnumerable<T> GetServices<T>(this IServiceProvider provider)
{
    if (provider == null)
    {
        throw new ArgumentNullException(nameof(provider));
    }

    return provider.GetRequiredService<IEnumerable<T>>();
}
/// <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>
/// <returns>An enumeration of services of type <paramref name="serviceType"/>.</returns>
public static IEnumerable<object?> GetServices(this IServiceProvider provider, Type serviceType)
{
    if (provider == null)
    {
        throw new ArgumentNullException(nameof(provider));
    }

    if (serviceType == null)
    {
        throw new ArgumentNullException(nameof(serviceType));
    }

    Type? genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
    return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
}
Run Code Online (Sandbox Code Playgroud)

源代码