skip to Main Content

Is there a way in ASP.NET to get all singletons?

I want to implement a health check which would instantiate all the registered singletons and only after that return a healthy status.

I am trying to get all the singletons from the IServiceProvider, but it seems to have only one method, i.e. the object? GetService(Type serviceType);.

So, maybe someone knows a way in C# to get all singletons?

I am trying to do like so:

_servicesProvider
  .GetServices<object>()
  .Where(service => _servicesProvider.GetService(service.GetType()) == service)

But it seems to also give me back scoped services.

Seems like this:

_servicesProvider
  .GetServices<object>()
  .ToList();
return new HealthCheckResult(HealthStatus.Healthy);

should work. But I am still hoping for a more optimal solution.

2

Answers


  1. I don’t believe there is a built in way to do this.

    This answer to a similar question agrees, and suggest reflection as a possible solution.

    Another option is to register your ServiceCollection with itself via .AddSingleton<IServiceCollection>(serviceCollection). You can then inject this or request it from the IServiceProvider and query it to get all the service descriptions with singleton lifetime:

    var serviceCollection = serviceProvider.GetRequiredService<IServiceCollection>();
    var singletons = serviceCollection
        .Where(descriptor => descriptor.Lifetime == ServiceLifetime.Singleton)
        .Select(descriptor => serviceProvider.GetRequiredService(descriptor.ServiceType))
        .ToList();
    
    foreach(object singleton in singletons)
    {
        // do something with the singleton
    }
    

    Depending on your use case it may be better to make your own implemention with a reduced interface, rather than exposing all of IServiceCollection to the consumer of the DI container.

    Login or Signup to reply.
  2. @DisplayName already gave an answer that might work as a starting point. Here is a different solution:

    IEnumerable<object> singletons = (
        from service in services
        group service by service.ServiceType into g
        where g.Any(descriptor => descriptor.Lifetime == ServiceLifetime.Singleton)
        from pair in serviceProvider.GetServices(g.Key).Zip(g)
        where pair.Second.Lifetime == ServiceLifetime.Singleton
        select pair.First)
        .Distinct();
    

    Compared to @DisplayName’s answer, this solution will retrieve all registered singletons, even those that are part of a collection. This solution, however, exhibits a few unfortunate downsides that might not exist in @DisplayName’s answer:

    1. If multiple registrations for the same service type exist, where the registrations contain -besides singletons- transient and/or scoped registrations, those registrations will be resolved from the container as well. They will be filtered out and not be part of the list of singletons, but they will still be created nonetheless. This might cause performance issues as the health check can now cause a lot of memory allocation (depending on the size of the application).
    2. This solution might not yield the correct results when used in combination with a DI Container that plugs into the MS.DI infrastructure (such as Autofac or Lamar). They enable features like Dynamic Interception and Decoration, and changes made specifically through the API of those containers might be missed.

    Pick the solution that works best for your situation.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search