Dependency Injection with classes other than a Controller class

Robert Paulsen picture Robert Paulsen · May 12, 2016 · Viewed 49.5k times · Source

At this point I'm injecting things into my Controllers with ease, in some cases building my own ResolverServices class. Life is good.

What I cannot figure out how to do is get the framework to automatically inject into non-controller classes. What does work is having the framework automatically inject into my controller IOptions, which is effectively the configuration for my project:

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

    public MessageCenterController(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

I'm thinking whether I can do the same for for my own classes. I assume I'm close when I mimic the controller, like this:

public class MyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    }

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

I think where I'm failing is when I call it like this:

public void DoSomething()
{
    var helper = new MyHelper(??????);

    if (helper.CheckIt())
    {
        // Do Something
    }
}

The problem I have tracking this down is practically everything that talks about DI is talking about it at the controller level. I tried hunting down where it happens in the Controller object source code, but it gets kinda crazy in there.

I do know I can manually create an instance of IOptions and pass it to the MyHelper constructor, but it seems like I should be able to get the framework do that since it works for Controllers.

Answer

Robert Paulsen picture Robert Paulsen · May 30, 2017

Below is a working example of using DI without anything that involves MVC Controllers. This is what I needed to do to understand the process, so maybe it will help somebody else.

The ShoppingCart object gets, via DI, an instance of INotifier (which notifies the customer of their order.)

using Microsoft.Extensions.DependencyInjection;
using System;

namespace DiSample
{
    // STEP 1: Define an interface.
    /// <summary>
    /// Defines how a user is notified. 
    /// </summary>
    public interface INotifier
    {
        void Send(string from, string to, string subject, string body);
    }

    // STEP 2: Implement the interface
    /// <summary>
    /// Implementation of INotifier that notifies users by email.
    /// </summary>
    public class EmailNotifier : INotifier
    {
        public void Send(string from, string to, string subject, string body)
        {
            // TODO: Connect to something that will send an email.
        }
    }

    // STEP 3: Create a class that requires an implementation of the interface.
    public class ShoppingCart
    {
        INotifier _notifier;

        public ShoppingCart(INotifier notifier)
        {
            _notifier = notifier;
        }

        public void PlaceOrder(string customerEmail, string orderInfo)
        {
            _notifier.Send("[email protected]", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
        }

    }

    public class Program
    {
        // STEP 4: Create console app to setup DI
        static void Main(string[] args)
        {
            // create service collection
            var serviceCollection = new ServiceCollection();

            // ConfigureServices(serviceCollection)
            serviceCollection.AddTransient<INotifier, EmailNotifier>();

            // create service provider
            var serviceProvider = serviceCollection.BuildServiceProvider();

            // This is where DI magic happens:
            var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);

            myCart.PlaceOrder("[email protected]", "2 Widgets");

            System.Console.Write("Press any key to end.");
            System.Console.ReadLine();
        }
    }
}