Net Core Dependency Injection for Non-Controller

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.


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));


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;
}