Microsoft Dependency Injection

Ok so to review a bit of the last post: we talked about the design decisions and principles that go into Dependency Injection. Separating the task of constructing the dependencies from using them was a key component. This can lead to a chicken-and-egg like problem: If it’s not the parent object’s job to construct the dependency, and it’s not the grandparent job to make it then who actually constructs the dependency? That defines the problem, let’s look at one possible solution, a library from Microsoft available on NuGet as Microsoft.Extensions.DependencyInjection.

Before we get to the problem though, let’s lay out the scenario. Say I was tasked with building the control systems for an automated factory that focuses on one model of car. For our purposes I’m assuming I’ve split the part inventories into 3 groups: engine parts, body or frame parts and finishing parts (trim, seats, etc), not terribly realistic but it keeps the examples short. With parts from these 3 lists, the factory can assemble cars. Before looking into dependency injection I might have created and populated these lists inside the factory’s constructor. As we talked about previously, this would be a pain to test and leave the factory too tightly coupled to them to adapt to changes. Now though, I look at it and decide to implement constructor injection to outsource the dependency injection:

class CarFactory {
    public CarFactory(IEngineInventory engineInventory, 
        IFrameInventory frameInventory, 
        IFinishingInventory finishingInventory)
    {..}
    public BuildCars()

It doesn’t look bad, and easy to drop in mock objects for testing, but we’re back to creating those inventory objects in Program.Main manually:

Program.Main() {
    var engineInventory    = new EngineInventory();
    var frameInventory     = new FrameInventory();
    var finishingInventory = new FinishingInventory();
    var factory = new CarFactory(engineInventory, frameInventory, 
        finishingInventory);
    factory.BuildCars();
}

That looks a little clunky, any changes to the objects mean we’re making changes to Program.Main which is difficult to test outside of production. We could wrap in it’s own object that creates all the items, but then we’ve just made that class tightly coupled to all of them? We don’t want that, nor do we want to create them inside Program.Main.

In comes Microsoft’s DependencyInjection library (which I’ll refer to as ms-di). It lets you define a list of concrete implementation types to return when asked for something that fulfills an interface’s requirements. What’s even fancier, is that if that object’s constructor references something it already knows about, it can automatically populate the constructor as well! Before I give you an example, I should mention, that what I’ve been calling objects or dependencies, it calls services:

Program.Main() {
    // First: create & populate the collection of service definitions
    var serviceList = new ServiceCollection();
    serviceList.AddSingleton<IEngineInventory,EngineInventory>();
    serviceList.AddSingleton<IFrameInventory,FrameInventory>();
    serviceList.AddSingleton<IFinishingInventory,FinishingInventory>();
    serviceList.AddSingleton<IVehicleFactory,CarFactory>();

    // Second: build an object to provide instances of those services
    var provider= serviceList.BuildServiceProvider();
    var factory = provider.GetService<IVehicleFactory>();
    factory.BuildCars();
}

That still leaves the service definitions in Program.Main, which seemed like too much low-level awareness at such a level. After some discussion with my mentors I discovered that ASP.Net has a convention where a Startup class is used to encapsulate such details in a common style. I like that idea and I swiped it, leaving me creating a Startup that creates the ServiceCollection fully configured that I call from Program.Main, then continue with the rest.

Another detail, for my example I’m using a single type of Add method for the service collection. There are 3 available when used in a console project that are based on the lifetime of the object. ASP.Net provides other Add methods, which relate more to how the object will be used, but the ones available for this purpose are:

  • AddSingleton – It creates one instance then returns that each time
  • AddTransient – Every time you request one, it returns a new instance
  • AddScoped – You can create a scope (see below), in which all scoped services act like singletons (without changing transient behavior). Outside that scope though, it recreates the services.

Defining a custom scope is fairly easy, ServiceProvider.CreateScope() returns an object that has a new copy of the ServiceProvider in it. Once you are finished with it, you can easily dispose of it, and create a new scope for another section of the code.

One additional note, it’s possible to pass delegates to all of the ServiceCollection.Add methods, as long as the delegate returns the type in question so it can figure out how to use it. Every usage of this I’ve explored has ended up refactored away as I understood how to use it better, but the capability is there.

So how does this help me maintain it?

In our scenario, once finished we have a factory up and going, say in the US somewhere. Then we decide to expand to another country, such as Japan. Accounting and inventory tracking requirements there may be different enough that we can’t use our existing inventory implementations. Not to worry, we can add a set of inventory objects targeted at Japan. If you’ve kept your coupling down to what’s absolutely necessary, all you need to do from there is change the service collection definitions to JapanEngineInventory, JapanFrameInventory and JapanFinishingInventory and retest.

Things you may try that won’t work (with alternatives)

  • Pass something to GetService to use to pick which implementation of an interface to return.
    • Building from the example, let’s say we don’t just have a CarFactory that inherits from IVehicleFactory but also a TruckFactory. I can add AddSingleton<IVehicleFactory,TruckFactory>() but GetService doesn’t accept arguments to pick which one returns (it always returns the last, yeah I tried this).
    • If you actually want to do this, then you need to wrap the subclasses in their own (possibly empty) interfaces, TruckFactory inerits from ILargeVehicleFactory which inherits from IVehicleFactory and similarly through CarFactory via ISmallVehicleFactory. Then you can add the ILargeVehicleFactory and ISmallVehicleFactory to the service collection and use those in the code.
  • Edit the service definitions on the fly and have your ServiceProvider see the changes
    • It doesn’t work. It shouldn’t work. Define your services higher up so you don’t even need to consider this. If you find a way to do this, and actually use it in real code, you’re a bad person. I say this having found a way to somewhat get around this. The code was unintelligible, the “code smell” was that of a landfill on week 2 of being on fire. I deleted my experiment and told myself to forget anything more than: don’t do it.

Leave a comment