web-dev-qa-db-fra.com

Remplacer l'enregistrement du service dans le conteneur DI intégré ASP.NET Core?

Considérons un enregistrement de service dans Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IFoo, FooA>();
}

Est-il possible de changer l'enregistrement de IFoo en FooB après l'appel de AddTransient? Il peut être utile à des fins de test (par exemple, dans la sous-classe TestStartup) ou si notre accès à la base de code est limité.

Si nous enregistrons une autre implémentation de IFoo:

services.AddTransient<IFoo, FooA>();
services.AddTransient<IFoo, FooB>();

Alors GetService<IFoo> Renvoie FooB au lieu de FooA:

IFoo service = services.BuildServiceProvider().GetService<IFoo>();
Assert.True(service is FooB);

Cependant, GetServices<IFoo> Renvoie avec succès les deux implémentations (et la même chose pour GetService<IEnumerable<IFoo>>):

var list = services.BuildServiceProvider().GetServices<IFoo>().ToList();
Assert.Equal(2, list.Count);

Il existe une méthode Remove(ServiceDescriptor) dans le contrat IServiceCollection. Que dois-je faire avec ServiceDescriptor pour modifier un enregistrement de service?

29
Ilya Chumakov

C'est simple en utilisant la méthode Replace(IServiceCollection, ServiceDescriptor) de la classe ServiceCollectionDescriptorExtensions .

// IFoo -> FooA
services.AddTransient<IFoo, FooA>();

// Replace
// IFoo -> FooB
var descriptor =
    new ServiceDescriptor(
        typeof(IFoo),
        typeof(FooB),
        ServiceLifetime.Transient);
services.Replace(descriptor);

Voir également:

40
Dustin Kingen

Il est facile de remplacer la fonctionnalité DI ASP.NET Core si vous savez deux choses simples:

1. ServiceCollection est juste un wrapper au-dessus de List<ServiceDescriptor>:

    public class ServiceCollection : IServiceCollection
    {
        private List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
    }

2. Lorsqu'un service est enregistré, un nouveau descripteur est ajouté à la liste :

    private static IServiceCollection Add(
        IServiceCollection collection,
        Type serviceType,
        Type implementationType,
        ServiceLifetime lifetime)
    {
        var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
        collection.Add(descriptor);
        return collection;
    }

Par conséquent, il est possible d'ajouter/supprimer des descripteurs à/de cette liste pour remplacer l'enregistrement:

IFoo service = services.BuildServiceProvider().GetService<IFoo>();
Assert.True(service is FooA);

var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IFoo));
Assert.NotNull(descriptor);
services.Remove(descriptor);

service = services.BuildServiceProvider().GetService<IFoo>();
Assert.Null(service);

Nous finissons par Replace<TService, TImplementation> méthode d'extension:

services.Replace<IFoo, FooB>(ServiceLifetime.Transient);

Sa mise en œuvre:

public static IServiceCollection Replace<TService, TImplementation>(
    this IServiceCollection services,
    ServiceLifetime lifetime)
    where TService : class
    where TImplementation : class, TService
{
    var descriptorToRemove = services.FirstOrDefault(d => d.ServiceType == typeof(TService));

    services.Remove(descriptorToRemove);

    var descriptorToAdd = new ServiceDescriptor(typeof(TService), typeof(TImplementation), lifetime);

    services.Add(descriptorToAdd);

    return services;
}
26
Ilya Chumakov