Are you sick of hearing the term "Dependency Injection" and immediately visualizing complex web apps with flashy user interfaces? You're in for a treat, my fellow developers, because this post will show you how to use Dependency Injection in terminal apps!

"Console apps?" you might be wondering. Isn't that just for basic command-line utilities?" But wait a minute. Your basic console app can benefit from the same power and flexibility that ASP.NET Core developers have for their web applications thanks to Dependecy Injection. Doesn't it sound good? Let's get started and explore how we can make even the most basic console apps flexible and testable.

In this article, I'll walk you through the process of creating a basic console app with a ServiceCollection that any ASP.NET Core developer would recognize.

Adding NuGet packages

You must install the Microsoft.Extensions package.In your console program, include the DependencyInjection package.

Using the.NET CLI

dotnet package addition Microsoft.Extensions.Using Visual Studio to Implement Dependency Injection

Starting with Program.cs
When you first start a console app, the boilerplate code will include a Main method that prints "Hello, World!" to the console. Inside this method, we'll remove the Console.WriteLine() method and put up our own programs. My preference is to build a new class called "Application" in which my app's logic will reside. Then, Program.cs just bootstraps the Application class with dependency injection.

using Microsoft.Extensions.DependencyInjection
namespace MyApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var services = CreateServices();

            Application app = services.GetRequiredService<Application>();
            app.MyLogic();
        }

        private static ServiceProvider CreateServices()
        {
            var serviceProvider = new ServiceCollection()
                .AddSingleton<Application>(new Application())
                .BuildServiceProvider();

            return serviceProvider;
        }
    }

    public class Application
    {
        public void MyLogic()
        {
            // Do something epic
        }
    }
}

Let's take a look at what's going on. On line 9, we set a ServiceProvider variable, which is the output of the CreateServices() function. We register a single service in the collection using this way. Because we'll be passing application flow to the Application class, a singleton instance is the best choice.

Line 11 begins the application logic by using the dependency injection container to resolve our singleton. In the Application class, you might wish to have a method called "Start", "Initialize", or something similar.

Following steps
This structure provides us with a versatile basis on which to build. In the following sections, we'll look at how we can enhance this and add certain components that you'll most likely want in your advanced console app.

Making Use of EF Core

When we install EF Core, we usually have to retrieve the connection string from IConfiguration, add our database context to the DI container, and perhaps run EF migrations when the application starts. Let's see how we can do this.

Add NuGet packages
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.Configuration.UserSecrets

Modifications to Program.cs
Don't forget about the new using statements that allow us to interact with EF Core and IConfiguration! There are two significant modifications to be aware of.

  • An IConfiguration is produced in CreateServices, and for demonstration purposes, I'm assuming my connection string is saved in my User Secrets file. The MyDbContext class is now registered to the ServiceCollection. Again, I'm using the Sqlite service for demonstration purposes.
  • Before beginning my Application class in the Main function, we request an instance of MyDbContext to perform database migrations. This ensures that when the logic of our app begins, the database is in the correct condition.

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection

namespace MyApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var services = CreateServices();

            MyDbContext context = services.GetRequiredService<MyDbContext>();
            context.Database.Migrate();

            Application app = services.GetRequiredService<Application>();
            app.MyLogic();
        }

        private static ServiceProvider CreateServices()
        {
            var configuration = new ConfigurationBuilder()
                .AddUserSecrets(Assembly.GetExecutingAssembly())
                .Build();

            string connectionString = configuration.GetConnectionString("MyApp");

            var serviceProvider = new ServiceCollection()
                .AddDbContext<MyDbContext>(options =>
                {
                    options.UseSqlite(connectionString);
                })
                .AddSingleton<Application>(new Application())
                .BuildServiceProvider();

            return serviceProvider;
        }
    }

    public class Application
    {
        public void MyLogic()
        {
            // Do something epic
        }
    }
}

ILogger is used for logging
Another popular component you may want to include in your console app is modern logging to replace all of those Console.WriteLine methods are used. We'll utilize the console logging provider as an example.

Add NuGet packages
dotnet add package Microsoft.Extensions.Logging.Console

Changes to Program.cs
On line 20, we're telling the dependency injection container that we're adding logging support. This comes from the using statement of Microsoft.Extensions.Logging. Then, I've modified the Application class so its constructor will receive an instance of ILogger which will come from our configured DI container. Take notice of how nothing needed to be changed on line 13 and 14. This is because our DI container will give us our configured singleton of Application, now with the ILogger<Application> included!

using System;
using Microsoft.Extensions.DependencyInjection
using Microsoft.Extensions.Logging;

namespace MyApp
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var services = CreateServices();

            Application app = services.GetRequiredService<Application>();
            app.MyLogic();
        }

        private static ServiceProvider CreateServices()
        {
            var serviceProvider = new ServiceCollection()
                .AddLogging(options =>
                {
                    options.ClearProviders();
                    options.AddConsole();
                })
                .AddSingleton<Application>(new Application())
                .BuildServiceProvider();

            return serviceProvider;
        }
    }

    public class Application
    {
        private readonly ILogger<Application> _logger;

        public Application(ILogger<Application> logger)
        {
            _logger = logger;
        }

        public void MyLogic()
        {
            _logger.LogInformation("Hello, World!");
        }
    }
}

HostForLIFE ASP.NET Core Hosting

European best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.