'How do I use .Net Core Dependency Injection from within a custom Class

Bit of a newbie question. I am having trouble getting access to dependency injected services from within my own custom class in ASP.NET Core 3.1

I can access services fine from within a controller or razor page e.g. I can get hold of configuration and data context information:

public class DetailModel : PageModel
{
    private readonly MyDataContext  _context;
    private readonly IConfiguration _config;

    public DetailModel(MyDataContext context, IConfiguration config)
    {
        _context = context;
        _config = config;   
    }

etc......

 }

I now wish to access these from the constructor of a custom class that is not a controller or razor page. e.g. I am using:

public class ErrorHandling
{
    private readonly MyDataContext  _context;
    private readonly IConfiguration _config;


    public ErrorHandling(MyDataContext context, IConfiguration config)
    {
        _context = context;
        _config = config;   

    }
 }

The problem is that when I instantiate my class it insists on me passing the service values into the constructor:

var myErrorHandler =  new ErrorHandling(`<wants me to pass context and config values here>`)

This defeats the whole point of DI. I think I am missing something fundamental here!

What am I missing?



Solution 1:[1]

You can register ErrorHandling as a service too, in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // other stuff..
    services.AddScoped<ErrorHandling>(); // this should work as long as both 'MyDataContext' and 'IConfiguration' are also registered
}

If you need an instance of ErrorHandling in your page model, you can specify it in the constructor and ASP.NET Core will resolve it for you at runtime.

This way you won't have to new it:

public class DetailModel : PageModel
{
    private readonly MyDataContext  _context;
    private readonly IConfiguration _config;
    private readonly ErrorHandling _errorHandling;

    public DetailModel(ErrorHandling errorHandling, MyDataContext context, IConfiguration config)
    {
        _context = context;
        _config = config;   
        _errorHandling = errorHandling;
    }

 }

This article can be useful: Dependency injection in ASP.NET Core

Solution 2:[2]

If you don't want register as a service, you can use ActivatorUtilities.CreateInstance to resolve ErrorHandling.

Instantiate a type with constructor arguments provided directly and/or from an IServiceProvider.

e.g.:

// IServiceProvider serviceProvider = ...;
var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(serviceProvider);

BUT you need to be careful about this solution:

  1. ServiceProvider scope should equal with dependency object (MyDataContext, IConfiguration). Otherwise, you will get an exception like:

    var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(app.ApplicationServices);
    // An exception of type 'System.InvalidOperationException' occurred 
    // in Microsoft.Extensions.DependencyInjection.dll but was not handled in user cod
    // e: 'Cannot resolve scoped service 'three.MyDataContext' from root provider.'
    

    For this, you can create an scope to resolve ErrorHandling:

    using (var scope = app.ApplicationServices.CreateScope())
    {
        var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
    }
    
  2. Dependency injection service would not call Dispose on IDisposable instances even out of scope.

    For this, you should call Dispose() by yourself:

     using (var scope = app.ApplicationServices.CreateScope())
     {
         using var disposablClass = ActivatorUtilities.CreateInstance<DisposablClass>(scope.ServiceProvider);
     }
    
  3. ActivatorUtilities.CreateInstance will new an instance even you use the same ServiceProvider:

    using (var scope = app.ApplicationServices.CreateScope())
    {
        var errorHandling1 = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
        Console.WriteLine(errorHandling1.GetHashCode());
        // 11903911
    
        var errorHandling2 = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
        Console.WriteLine(errorHandling2.GetHashCode());
        // 40026340
    }
    

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nkosi
Solution 2 bnet