Skip to content

[API Proposal]: OnBeforeBuild Delegate - Add a hook into the ServiceCollection, allowing execution of code just before building the ServiceProvider #84847

@thomhurst

Description

@thomhurst

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-Extensions-DependencyInjectionneeds-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions