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

前端 未结 3 1581
悲哀的现实
悲哀的现实 2020-12-05 17:15

Let us consider a service registration in Startup.ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.A         


        
相关标签:
3条回答
  • 2020-12-05 17:37

    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
    0 讨论(0)
  • 2020-12-05 17:50

    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;
    }
    
    0 讨论(0)
  • 2020-12-05 17:53

    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.Singleto)
        .BuildServiceProvider();
    
    0 讨论(0)
提交回复
热议问题