How to take advantage of Dependency Injection in .Net Core 2.2 + Console Applications

This article is the first of a multi-part series on building console applications in .Net Core.

Why Bother?

I’ve been working on a machine learning application for work. As with anything machine learning related, you need lots and lots of data. Unfortunately, mine is in a truly messed up format. Rather than use all sorts of convoluted approaches in Python, I decided to do this in C# because:

  1. I do most of my work in C#, so it is easier for me.
  2. I think pulling SQL Server data from python is a major pain in the ass.
  3. I want to play with ML.Net so keeping things in C# is reasonable
  4. Because I want to 😝

So what does this have to do with console apps and .net core?

Relax, I’m getting there. I decided to build a little console application to take a table and transpose it, because the number of fields I have to deal with made doing it via SQL Server’s Pivot/Unpivot function extremely unappetizing. However, once I went down this rabbit hole I discovered that .Net Core doesn’t really like you being able to take advantage of all of its power for a lowly console application.

For example, I like dependency injection. I think it leads to very clean code that isn’t cluttered up by a bunch of object creations and a bunch of configuration setting fetches, etc. Plus if you are a fan of TDD (I am) it is much easier to create mocks and stubs for all sorts of fun.

Did I need to do this? Could I have coded around it? Yes, of course. However, that would have led to some really brittle code, which I wouldn’t have wanted to put my name on. So I set about figuring out how to do this in a console app.

Specifically, I wanted to do the following things:

  1. Use the dependency injector that comes built into .net core
  2. Pull stuff from configuration files
  3. Use logging out of the box
  4. Easily manage my database contexts

This article will cover step one. Articles describing the other 3 features will shortly follow.

So after poking around on the internet a bit, I came to the conclusion that the best approach is to essentially mimic much of what a web application does on start up.

Would you please shut up and start coding already?

OK! OK! Keep your shirt on! A couple of assumptions here:

  • You are using Visual Studio 2017 or later. You can use VSCode if you want, but I am not going to walk through getting that all configured and happy. Microsoft spent millions building other very nice tools! I’m using them.
  • You are using .net core 2.2 or later.
  • You are using C#. VB.Net has its place (in hell) as does F# and other .net languages. But that’s not what I am doing here
  • You know what the hell dependency injection is. If not, go learn about it. Then come back.
  • You can manage your NuGet packages yourself, like a grown up. I won’t be providing screenshots showing you how to do that. Someone else already did that. Go find their shit. Don’t know how to use Google? Please stop reading. This won’t be fun for you.

Step 1: Getting Set up

So first things first. Create a .net Core Console Application. Next, we need to add the dependency injection package in order to get up and going. Go to Manage NuGet packages and add the following:

  • Microsoft.Extensions.DependencyInjection

Step 2: Dependency Injection

So now, we want to set up Microsoft’s handy little dependency injection container. This works fine for small to medium sized programs. If you want, you can swap it out for one of the more powerful containers, but that’s beyond the score of this article.

At this point, we’ve created an empty console app. Open up your program.cs file.

Initially, what you will see is:

static void Main(....)
{
}

Lets lay the foundation for our dependency injection. As in the web application world, we want to configure our various services, instantiate a provider, and then run our application from its entry point.

Since we are building a mini “framework” we will define a class outside of Program called ConsoleApplication.cs. This is where we will begin execution of our actual program:

public class ConsoleApplication
{
public void Run();
}

If you wish, you could pass command line parameters to the Run method, but for this example I won’t bother. Similarly, you could make ConsoleApplication implement an IApplication interface for use by TDD (for example), but since all I intend to do is use Run as my console application equivalent of main, I wont write unit tests for the Program class, so I am not going to bother.

With the housekeeping out of the way, we need to import the dependency injection package that we added, so at the top of your Program.cs file add:

using Microsoft.Extensions.DependencyInjection;

In Main, we will perform the three steps mentioned above.

  1. Configure Services
  2. Create Provider so we can use dependency injector
  3. Kick off Run Method.

Our Main will look like:

static void Main
{
// Create service collection and configure our services
var services = ConfigureServices();
    // Generate a provider
var serviceProvider = services.BuildServiceProvider();

// Kick off our actual code
serviceProvider.GetService<ConsoleApplication>().Run();
}

The last line is where we actually grab an instance of our ConsoleApplication class and ask it to do the work of the application (otherwise this is all pretty useless).

Before we inject any dependencies, lets define a couple dependencies to inject:

public interface ITestService
{
void DoSomethingUseful();
}
public class TestService : ITestService
{
public void DoSomethingUseful()
{
......
}
}

Next, we need to implement ConfigureServices. Here, we will set up dependency injection. Later, we can add in our own services as needed. We will expand on this in later articles to add in logging and database support.

private static IServiceCollection ConfigureServices()
{
IServiceCollection services = new ServiceCollection();
    services.AddTransient<ITestService, TestService>();
    // IMPORTANT! Register our application entry point
services.AddTransient<ConsoleApplication>();
    return services;
}

Notice that we register our ConsoleApplication class as well. You could do this as a singleton if you want, but I will leave that to you. If you forget to register ConsoleApplication though, your application will do a whole lot of nothing.

Now, we have it set up to easily injection instances of ITestService.

From here, we just need to set up our ConsoleApplication class for proper injection and away we go:

public class ConsoleApplication
{
private readonly ITestService _testService;
    public ConsoleApplication(ITestService testService)
{
_testService = testService;
}

// Application starting point
public void Run()
{
... Do some real work
_testService.DoSomethingUseful();
    }
}

This is pretty clean. We inject our Test service object and then store it in a member variable. Then we go ahead and use it!

There we go! Basic dependency injection configured within a Console application with relatively little pain.

In a separate article, I will explain adding logging and database connections to this simple framework so you can build cleaner and more manageable console applications that support testing.

Have fun coding!