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?
It is easy to override ASP.NET Core DI functionality if you know two simple things:
1.
ServiceCollection
is just a wrapper on top ofList<ServiceDescriptor>
:2. When a service is registered, a new descriptor is added to list:
Therefore, it is possible to add/remove descriptors to/from this list to replace the registration:
We finish with
Replace<TService, TImplementation>
extention method:Its implementation:
This is simple using the
Replace(IServiceCollection, ServiceDescriptor)
method from theServiceCollectionDescriptorExtensions
class.See also: