-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Background and motivation
While the Microsoft Dependency Injection container works extremely well for most use cases, it has a couple of weaknesses compared to some other containers.
One area where I've specifically found that it is weak in, is supporting Decorator classes.
While it is currently possible to support decorators, it is dependent on the ordering of registrations. Meaning you can't register a decorator class before registering the original implementation. This means that if someone is unaware of this order, they could start refactoring the Dependency Injection setup, moving certain registrations into different locations, and this could end up breaking your application. Therefore this means this is delicate, flaky code.
A Dependency Injection container shouldn't really concern itself with the ordering of registrations in my opinion.
By exposing a delegate on the IServiceCollection, called something like OnBeforeBuild, we can add hooks that will be invoked just before building the ServiceProvider.
This means that regardless of when we called (for example) an extension method called .AddDecorator<IInterface, TDecorator>(), which under the hood used this hook, it wouldn't break, because it would end up always being invoked at the latest stage possible.
This hook could also enable more advanced use-cases, such as scanning the collection before building, and utilising that data in any custom logic.
My proposed API would look like this:
+ public delegate void OnBeforeBuild(IServiceCollection serviceCollection);
public interface IServiceCollection : IList<ServiceDescriptor>
{
+ OnBeforeBuild OnBeforeBuild { get; set; }
}Then where we are given a ServiceCollection, we can use this hook. We can then implement extension methods to abstract away logic like
public static class DependencyInjectionExtensions
{
public static IServiceCollection AddSingletonDecorator<TService, TDecorator>(this IServiceCollection services)
where TService : class
where TDecorator : class, TService
{
services.OnBeforeBuild += collection => collection.AddSingleton<TService, TDecorator>();
return services;
}
}API Proposal
+ public delegate void OnBeforeBuild(IServiceCollection serviceCollection);
public interface IServiceCollection : IList<ServiceDescriptor>
{
+ OnBeforeBuild OnBeforeBuild { get; set; }
}API Usage
services.OnBeforeBuild += collection => collection.AddSingleton<TService, TDecorator>();Alternative Designs
No response
Risks
Building the ServiceProvider would be marginally slower. But this is a one-time process for most applications.
This shouldn't cause any breaking changes, as it just exposes a new delegate, and doesn't change any existing behaviour.