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

Solution 1:

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

Solution 2:

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

Solution 3:

Just to add on @ilya-chumakov 's great answer, here is the same method but with support for implementation factories

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

    services.Remove(descriptorToRemove);

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

    services.Add(descriptorToAdd);

    return services;
}

in case we want to use it with a factory that instantiates the service like the following sample:

var serviceProvider = 
  new ServiceCollection()
    .Replace<IMyService>(sp => new MyService(), ServiceLifetime.Singleton)
    .BuildServiceProvider();

Solution 4:

In the latest version of .net core(net5.0), this is how it should be.

using Microsoft.Extensions.DependencyInjection;

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

Or try this one.

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