Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace service registration in ASP.NET Core built-in DI container?

Let us consider a service registration in Startup.ConfigureServices:

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

Is it possible to change IFoo registration to FooB after AddTransient has been called? It can be helpful for testing purposes (for example, in TestStartup subclass) or if our access to codebase is limited.

If we register another IFoo implementation:

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

Then GetService<IFoo> returns FooB instead of FooA:

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

However, GetServices<IFoo> successfully returns both implementations (and the same for GetService<IEnumerable<IFoo>>):

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

There is Remove(ServiceDescriptor) method in IServiceCollection contract. What should I do with ServiceDescriptor to modify a service registration?

like image 846
Ilya Chumakov Avatar asked Apr 24 '17 14:04

Ilya Chumakov


People also ask

Which built-in service container is provided by ASP.NET Core when the dependency is registered in the service container?

ASP.NET Core provides a built-in service container, IServiceProvider. Services are typically registered in the app's Startup. ConfigureServices method.

What is built-in IoC container in .NET Core?

ASP.NET Core framework includes built-in IoC container for automatic dependency injection. The built-in IoC container is a simple yet effective container.

What is difference between AddTransient and AddScoped?

AddTransient() - This method creates a Transient service. A new instance of a Transient service is created each time it is requested. AddScoped() - This method creates a Scoped service. A new instance of a Scoped service is created once per request within the scope.

Which are the ways dependent services can be registered with the container?

Registering Application Service ASP.NET Core allows us to register our application services with IoC container, in the ConfigureServices method of the Startup class. The ConfigureServices method includes a parameter of IServiceCollection type which is used to register application services.


2 Answers

This is simple using the Replace(IServiceCollection, ServiceDescriptor) method from the ServiceCollectionDescriptorExtensions class.

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

See also:

  • ServiceDescriptor constructors
like image 170
Dustin Kingen Avatar answered Sep 19 '22 13:09

Dustin Kingen


It is easy to override ASP.NET Core DI functionality if you know two simple things:

1. ServiceCollection is just a wrapper on top of List<ServiceDescriptor>:

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

2. When a service is registered, a new descriptor is added to list:

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

Therefore, it is possible to add/remove descriptors to/from this list to replace the registration:

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

We finish with Replace<TService, TImplementation> extention method:

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

Its implementation:

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; } 
like image 29
Ilya Chumakov Avatar answered Sep 17 '22 13:09

Ilya Chumakov