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

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

 public void ConfigureServices(IServiceCollection services) { services.AddTransient(); } 

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

Si nous enregistrons une autre implémentation IFoo :

 services.AddTransient(); services.AddTransient(); 

Ensuite, GetService renvoie FooB au lieu de FooA :

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

Cependant, GetServices renvoie correctement les deux implémentations (et la même chose pour GetService<IEnumerable> ):

 var list = services.BuildServiceProvider().GetServices().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?

Ceci est simple à l’aide de la méthode Replace(IServiceCollection, ServiceDescriptor) de la classe ServiceCollectionDescriptorExtensions .

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

Voir également:

  • Constructeurs ServiceDescriptor

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

1. ServiceCollection est juste un wrapper au-dessus de la List :

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

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’append / supprimer des descripteurs à / de cette liste pour remplacer l’enregistrement:

 IFoo service = services.BuildServiceProvider().GetService(); Assert.True(service is FooA); var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IFoo)); Assert.NotNull(descriptor); services.Remove(descriptor); service = services.BuildServiceProvider().GetService(); Assert.Null(service); 

Nous finissons avec la méthode d’extention Replace :

 services.Replace(ServiceLifetime.Transient); 

Sa mise en œuvre:

 public static IServiceCollection Replace( 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; }