Net Core Dependency Injection for Non-Controller

12,722

Solution 1

Just make the class a service.

In startup.cs

services.AddScoped<AccountBusinessLayer>();

Then in controller, same as you do for other services:

private readonly AccountBusinessLayer _ABL;

Include in constructor as you do for other services:

 public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,IOptions<IdentityCookieOptions> identityCookieOptions,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory,
    RoleManager<IdentityRole> roleManager,
    AccountBusinessLayer ABL
  )
{
  _userManager = userManager;
  _signInManager = signInManager;
  _externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
  _emailSender = emailSender;
  _smsSender = smsSender;
  _logger = loggerFactory.CreateLogger<AccountController>();
  _roleManager = roleManager;
  _ABL = ABL;
}

Solution 2

You can easily define a static class with one property like:

public static class StaticServiceProvider
{
    public static IServiceProvider Provider { get; set; }    
}

after defined class you have to scope the service in the Startup.ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    //TODO: ...

    services.AddScoped<IUnitOfWork, HttpUnitOfWork>();            
    services.AddSingleton<ISomeInterface, ISomeImplementation>();
}

then inside the Startup.Configure method on startup you can set the provider as static class property:

public void Configure(IApplicationBuilder app, ...)
{
    StaticServiceProvider.Provider = app.ApplicationServices;

    //TODO: ...
}

Now you can easily call StaticServiceProvider.Provider.GetService method almost everywhere in your application:

var unitOfWork = (IUnitOfWork)StaticServiceProvider.Provider.GetService(typeof(IUnitOfWork));

Solution 3

I'm not sure this is the best answer, but the way I decided to do it is to do the following:

1) Per the answer by @BrunoLM at on this question Resolving instances with ASP.NET Core DI suggested by @SystemCrash, I created a new project called UnderstandingDependencyInjection and pasted in the code examples.

Important: What I describe next see next will not make sense unless you visit the referenced link above (#1). What you see below is a partial solution that builds on the answer another user provided in a another SO question.

2) Next, I created another class called OtherService. I added a method DoSomething() that took a dependency on the TestService.

3) In the constructor of OtherService, I requested IServiceProvider in order to get a concrete implementation of ITestService so I could call its GenerateRandom() method.

4) Back in the HomeController.cs, I merely passed along the IServiceProvider reference to the constructor of OtherService.

So, this is what I have:

OtherService.cs

using System;
using Microsoft.Extensions.DependencyInjection;

namespace UnderstandingDependencyInjection.Services
{
    public class OtherService
    {
        private readonly ITestService _testService;

        public OtherService(IServiceProvider serviceProvider)
        {
            _testService = serviceProvider.GetService<ITestService>();
        }

        public int DoSomething()
        {
            var rnd = _testService.GenerateRandom();
            return rnd * 2;
        }
    }
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;
using UnderstandingDependencyInjection.Services;

namespace UnderstandingDependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private readonly ITestService _testService;
        private readonly IServiceProvider _serviceProvider;

        public HomeController(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
            _testService = serviceProvider.GetService<ITestService>();
        }

        public IActionResult Index()
        {
            // This works!
            // var rnd = _testService.GenerateRandom();

            // What if I need to reference the TestService 
            // from another service? I.e., OtherService?
            var otherService = new OtherService(_serviceProvider);

            var rnd = otherService.DoSomething();

            ViewBag.RandomNumber = rnd;
            return View();
        }

So, to summarize, the key to this technique is to pass around the concrete reference of IServiceProvider that your controller receives ... passing from the controller into any other custom classes that will also need any services that are registered into ASP.NET Core's DI framework.


What about static methods that depend on the TestService?

But, I may not want / need to create an instance of OtherService. I may want to merely call a method statically, but that method takes a dependency on a service managed by ASP.NET Core MVC's Dependency Injection framework. What now?

In this case, the best I can figure out, you would need to pass in the reference ON THE METHOD CALL to the static method. It looks nasty, and I'm hoping there's a more elegant way ... but here's what I figured out.

5) Building on the previous steps (above) I added a new class called StaticService.

6) I created a method DoSomething that takes IServiceProvider as a parameter.

7) I use the concrete instance of the IServiceProvider to get a concrete instance of the ITestService. I use this to call GenerateRandom().

8) From the controller, call the StaticService.DoSomething() method passing it the concrete instance of IServiceProvider that I'm holding on to.

StaticService.cs

using Microsoft.Extensions.DependencyInjection;

namespace UnderstandingDependencyInjection.Services
{
    public class StaticService
    {
        // No constructors

        public static int DoSomething(IServiceProvider serviceProvider)
        {

            var testService = serviceProvider.GetService<ITestService>();
            var rnd = testService.GenerateRandom();
            return rnd * 3;
        }
    }
}

HomeController.cs

    public IActionResult Index()
    {
        // This works!
        // var rnd = _testService.GenerateRandom();

        // What if I need to reference the TestService 
        // from another service? I.e., OtherService?
        //var otherService = new OtherService(_serviceProvider);
        //var rnd = otherService.DoSomething();

        // What if I need to reference the TestService
        // from another service with a STATIC method?
        // Best I can tell, you have to pass the 
        // ServiceProvider in on the method call.
        var rnd = StaticService.DoSomething(_serviceProvider);

        ViewBag.RandomNumber = rnd;
        return View();
    }

But isn't passing around ServiceProvider an anti-pattern?

In short, yes. You wind up passing ServiceProvider around everywhere in code. Some would argue that this gives every controller and ever class access to every service registered in ASP.NET Core's DI. That's true, and that seems bad.

But what are your alternatives? Should every class that has a dependency on your service ALSO be defined as a service and registered with the DI? In other words, should I create IOtherService, and then pass it a concrete ITestService in its constructor?

I could do that, HOWEVER now my controller's constructor needs BOTH ITestService AND IOtherService. In other words, in order to work correctly, the Controller needs to know how OtherService does its job and that it uses ITestService internally. That seems bad, too.

What to do?


What's the Best Answer?

Frankly, I think the best answer is found here:

Passing Services using Dependency Injection and Factory Pattern in ASP.NET

@Steven says in his answer:

It does mean however that you might need to move away from the built-in DI container of ASP.NET Core to a more feature rich DI library, because the built-in container is not capable of making a context aware registration for ILogger while having the library auto-wire other constructor dependencies as well.

Share:
12,722

Related videos on Youtube

CCPony
Author by

CCPony

Updated on September 15, 2022

Comments

  • CCPony
    CCPony over 1 year

    Seems crazy that something like this is causing me such a headache. But here it is:

    How do you use the built-in dependency injection for net core for a non-controller class? Please provide an example with includes instantiation.

    Thanks.

    • Joe Audette
      Joe Audette over 7 years
      same way as with a controller, you put a dependency in the constructor of your class and you wire up the dependency in Startup.cs. there is nothing special about controllers in regards to DI
  • Baked Inhalf
    Baked Inhalf almost 4 years
    Tried this in non controller, doesn't work. Ctor of AccountBusinessLayer is never run when trying to inject other service.
  • johnye2e
    johnye2e over 3 years
    This does not answer the question.
  • John Arundell
    John Arundell over 3 years
    Perhaps I was not clear. Make the class that wants to consume an injected service a service too. Consumer = service => inject other service into consuming service.
  • Ben Sewards
    Ben Sewards over 2 years
    can this work in a .NET 5 isolated app? I can't seem to find IApplicationBuilder.