Does IServiceProvider.GetServices<T>() always returns the available service implementations in the registration order?

Viewed 1335

This question specifically refers to ASP.NET core 3.1 and the built-in dependency injection container (Microsoft DI).

This Microsoft documentation and this stackoverflow question confirm that the Microsoft DI container always resolves IEnumerable<TService> by respecting the registration order, when multiple implementation types are registered for the same service type. The order is guaranteed and this is clearly documented.

Does anyone know whether the same holds true for the IServiceProvider.GetServices<T>() method ?

If the answer to the above question is yes, does this holds true even in the following example (where two different instances of the same class are registered as implementations for the same service type) ?

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 ???
1 Answers

Short answer is yes since internally GetServices* extension methods resolves IEnumerable<T> same as in constructors that have IEnumerable<T> as injected dependency

/// <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);
}

Source code

Related