Click here to Skip to main content
15,881,092 members
Articles / Web Development / ASP.NET

Adding Dependency Injection to your .NET Console Apps (and Tests)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
7 Aug 2022CPOL3 min read 8.6K   5   3
How to add DI to your .NET console apps
The examples shown in this post will use Microsoft’s no-frills DI container that comes out of the box with ASP.NET Core projects. The concepts covered here will be similar for other containers, but the code itself will differ slightly.

You’ve probably noticed that Dependency Injection (DI) has become the defacto norm these days, and arguably rightfully so, since it (if used properly) leads to a testable, changeable, de-coupled codebase. But perhaps one challenge (at least in the ASP.NET ecosystem) is that because so much of the bootstrapping is taken care of when we create a new ASP.NET project, it can be confusing and unclear as to how to set up DI in your automated tests or other top-level projects that don’t get the out-of-the-box scaffolding that ASP.NET web projects do. There are a few guides on how to do this out there, but here’s a no-fluff minimalistic one.

Oh, before going any further, I’ll mention that the examples shown here will be using Microsoft’s no-frills DI container that comes out of the box with ASP.NET Core projects. The concepts covered here will be similar for other containers, but the code itself will differ slightly.

First, Let's Order Some Packages

Image 1

When it comes to versioning, chances are you’ve already got these installed in other projects in your solution. So as long as you’re not mixing and matching .NET versions between projects, it makes sense to keep the versions the same. For this reason, I’ll just list the packages you need and let you figure out the appropriate version.

  1. Microsoft.Extensions.Configuration
  2. Microsoft.Extensions.Configuration.Json
  3. Microsoft.Extensions.DependencyInjection
  4. Microsoft.Extensions.DependencyInjection.Abstractions
  5. Microsoft.Extensions.Options

Add Your App Settings

When it comes to configuration, you probably have some custom app settings defined in an appsettings.*.json file within your existing ASP.NET web project. You’ll need to get this into your new (bare bones) project for the same settings to apply. You have a few options for doing this:

  1. Create a custom settings file for your new project;
  2. Copy the existing appsettings.json file from your web project into your new one; or
  3. Add a link from the existing appsettings.json file to your new project

I usually prefer option 3, so any changes you make in the appsetting.json file in your main web project will also take effect in your new project, and you don’t need to maintain two (or more) config files.

Image 2

* The appearance of this may look slightly different in Rider or VS for Mac, but it should still be there.

Set It All Up

Here, I suggest creating a xxxBootstrapper class to handle the bootstrapping of the DI container and any other setup, where xxx is a prefix for your project. Example, if this is a test project, you could call this TestBootstrapper.

For example:

C#
public static class TestBootstrapper
{
    public static void Bootstrap(IServiceCollection services)
    {
        // todo: Add any test services
    }
}

That’s Great, but How Do I Actually Create the Iservicecollection Instance?

Ask politely :) Seriously though, just create a new ServiceCollection, which is the concrete implementation of IServiceCollection.

C#
public static class ServiceProviderFactory
{
    public static IServiceProvider CreateServiceProvider()
    {
        var services = new ServiceCollection();

        LibraryBootstrapper.Bootstrap(services);
        TestBootstrapper.Bootstrap(services);

        return services.BuildServiceProvider();
    }
}

Since we’ll probably be doing this in every single test and the bootstrapping logic will probably grow as our solution does, I’ve placed this inside a factory for convenience.

Putting It All Together

Here it is. :) Do note that Setup and Test are NUnit-specific attributes, which may differ from what you’ll need to use depending on the testing framework you’re using - although you ought to use NUnit because all the others are stupid.

*** The above statement is a joke. Use whichever testing framework you like.

C#
using ConsoleDependencyInjection.Library;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDependencyInjection.Tests;

public class MyServiceTests
{
    private IMyService _sut;

    [SetUp]
    public void Setup()
    {
        _sut = ServiceProviderFactory
            .CreateServiceProvider()
            .GetService<IMyService>();
    }

    [Test]
    public void Test1()
    {
        Assert.DoesNotThrow(() =>
        {
            _sut.DoTheThing();
        });
    }
}

In the Setup method (which runs before each test), we create an instance of the service provider from the factory we created earlier, and using a Service Locator approach to DI, ask the provider for the service we want to test. To see the whole thing in action, you can check out the repo on Github.

Thanks for reading! Catch ya!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Australia Australia
G'day guys! My name is Jason, and I'm a backend software engineer living in Sydney, Australia. I enjoy blogging, playing chess and travelling.

Comments and Discussions

 
QuestionBootstrappers. Pin
James Curran8-Aug-22 3:41
James Curran8-Aug-22 3:41 
QuestionPostscript? :) Are u sure? Pin
LightTempler7-Aug-22 5:02
LightTempler7-Aug-22 5:02 
Please recheck your language selection.
There are so less Postscript programmers left.
LiTe

AnswerRe: Postscript? :) Are u sure? Pin
Jason Sultana7-Aug-22 20:00
Jason Sultana7-Aug-22 20:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.