Resolving instances with ASP.NET Core DI in static classes

There is a way to resolve instances with ASP.NET Core Core Dependency in static classes when is no possible DI using ?
Yes, this guide would explain how to do this.
The idea behind this starts with an article on stackoverflow: Resolving instances with ASP.NET Core DI, that use a service provider to retrieve injected services.

Table of content

Prerequisites

  • A project targets to netcoreapp3.1 😉

Related articles

The only problem with that stackoverflow article is this:
to be able to resolve instances with ASP.NET Core DI in static classes, you need a full “service provider” that contains the services you already have added from your startup.cs class. Isn’t enough create a new instance of IServiceProvider and use it.

A recommendation:
This is not the best practice: you should use DI when DI is available, the purpose of this article is to give an hint in case when the DI cannot be used due to several reason.

The idea behind

Basically we have to find a way to be able to retrieve in any point of our application, on any application state a fully “IServiceProvider“.
To do that we are going to use a static IServiceProvider

Static member vs instance member

Basically instance member need “new operator” to be used. So you are going to create another instance of your object that not sharing anything with an already instanced object of same class.
Instead, static member, will be created only one time in memory and all caller that use this is sharing the same resource.

https://stackoverflow.com/questions/18400214/c-sharp-static-member-vs-instance-member
https://www.codeproject.com/Articles/19247/Static-Members-vs-Instance-Members-Overview

Create the “DI container”

  1. Create a new class, and call “ServiceActivator“.
  2. Define a static IServiceProvider:
internal static IServiceProvider _serviceProvider = null;
  1. Create a method that allow you to fill this static service provider with a fully one:
/// <summary>
/// Configure ServiceActivator with full serviceProvider
/// </summary>
/// <param name="serviceProvider"></param>
public static void Configure(IServiceProvider serviceProvider)
{
    _serviceProvider = serviceProvider;
}

  1. To be able to retrieve a service, you need to create an Microsoft.Extensions.DependencyInjection.IServiceScope which contains an System.IServiceProvider used to resolve dependencies from a newly created scope.
    (https://docs.microsoft.com/it-it/dotnet/api/microsoft.extensions.dependencyinjection.iservicescopefactory.createscope?view=dotnet-plat-ext-3.1)
    You are able to to this without any additional method, by using directly the member “internal static IServiceProvider _serviceProvider”, but could be useful hide some stuff behind a method, so define a it to create a scope in “clean” way:
/// <summary>
/// Create a scope where use this ServiceActivator
/// </summary>
/// <param name="serviceProvider"></param>
/// <returns></returns>
public static IServiceScope GetScope(IServiceProvider serviceProvider = null)
{
    var provider = serviceProvider ?? _serviceProvider;
    return provider?
        .GetRequiredService<IServiceScopeFactory>()
        .CreateScope();
}
  1. Your class should look like this:
using Microsoft.Extensions.DependencyInjection;
using System;

namespace ASPNETCore
{
    /// <summary>
    /// Add static service resolver to use when dependencies injection is not available
    /// </summary>
    public class ServiceActivator
    {
        internal static IServiceProvider _serviceProvider = null;

        /// <summary>
        /// Configure ServiceActivator with full serviceProvider
        /// </summary>
        /// <param name="serviceProvider"></param>
        public static void Configure(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        /// <summary>
        /// Create a scope where use this ServiceActivator
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <returns></returns>
        public static IServiceScope GetScope(IServiceProvider serviceProvider = null)
        {
            var provider = serviceProvider ?? _serviceProvider;
            return provider?
                .GetRequiredService<IServiceScopeFactory>()
                .CreateScope();
        }
    }
}

Configure the “DI Container”

  1. Go back to your startup.cs
  2. Look for “public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  3. add this line on top of your method: ServiceActivator.Configure(app.ApplicationServices);
  4. Your method should look like this:
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

    ServiceActivator.Configure(app.ApplicationServices);

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    // other Configure stuff ...
}

Use the “DI Container”

When you need to use it you have to:

  1. Create a new scope
  2. Retrieve your service. To do that you have two ways:
    1. Get a service by registered interface, using GetService(Type). In this case you have to do an explicit cast, because this one return an object. This is useful when you not have an interface to resolve

For example:

using (var serviceScope = ServiceActivator.GetScope())
{
    ILoggerFactory loggerFactory = serviceScope.ServiceProvider.GetService<ILoggerFactory>();
    IOptionsMonitor<JwtConfiguration> option = (IOptionsMonitor<JwtConfiguration>)serviceScope.ServiceProvider.GetService(typeof(IOptionsMonitor<JwtConfiguration>));

    /*
        use you services
    */
}

Source Code

7 Thoughts to “Resolving instances with ASP.NET Core DI in static classes”

  1. Eric Venneker

    Thank you. Although this is not a recommended practice (static + DI is apparently a no-no), I have a specific reason for requiring this solution.

    We are using a report suite that only allows you to call static functions from a report expression. And because the function has to be static, you cannot use DI to resolve (for example) translation services. Very annoying, but we have to work with what we got. This has solved my issue.

    Thanks again

    1. davide@davidezoccarato.cloud

      Eric, thank you for you feedback. You’re right: this is not the best practice, indeed the purpouse is to use this approach in case when DI cannot be applied due to some limitations.
      I’m going to update this article with your raccomandation!

      Davide

  2. Darko

    Thank you so much for the help here, i was working on global exception handler and i need it something that will get the login service inside there so i can write inside db and this helps a lot 🙂

    1. davide@davidezoccarato.cloud

      Hi Darko
      I’m glad I could help!

      Davide

  3. xetod

    great! your solution saved my all day. many thanks.

    1. davide@davidezoccarato.cloud

      @xetod, glad to be helpful!

  4. Thanks for sharing this

Leave a Comment