1

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?

2
  • 1
    Register ErrorHandling with the service collection and resolve it from the built provider. that will inject the desired dependencies Commented Nov 20, 2020 at 14:01
  • Where is it that you are trying to initialize the handler? Commented Nov 20, 2020 at 14:02

2 Answers 2

5

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

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks that works treat! The only problem is that the data context that I get from the error handling appears to reference the same context as used elsewhere in the page and doing SaveChanges() comits both which is not the desired behaviour.
Maybe the data context is registered as a Singleton (in Startup.cs)?
Thanks. It was registered as scoped, but changing it to transient fixed the issue. But I guess that might have performance issues elsewhere in the site.
0

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
    }
    

1 Comment

And why is calling ActivatorUtilities.CreateInstance bettern than new(...)? Particularly since the question is about how to use dependency injection, not how to create the instance.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.