Dependency Injection Container In Console App

I got tired of figuring this out everytime I had a play with something in a console application. DI is a great pattern to understand and use even when just having a play.

IHostBuilder

Examples based on Jonathan Williams - Dependency Injection In a C# Console Application (.NET Core)

Required nuget packages are Microsoft.Extensions.Hosting

Create services

These would be services you want to be injected into the dependency injection container and be avalible inside your application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ServiceA
{
private readonly ServiceB _serviceB;

public ServiceA(ServiceB serviceB)
{
_serviceB = serviceB;
}

public void DoSomething() => _serviceB.DoSomething();
}

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

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

public void DoSomething() => _logger.LogInformation("Service B is doing something.");
}

Create Host Builder

This will build the dependency injection container.

1
2
3
4
5
6
7
8
9
10
11
12
13
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddTransient<Program>();
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
});
}

Entry Point

Refactor Program.cs to have Program as public and have a constructor to accept instantiated objects.

Add a public Run() method to execute your service code. This will run after the dependency injection container has been built.

In Main which is the console applications entry point, create the host and run the program. Here call CreateHostBuilder which will return an IHost which contains all of the services.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Program
{
private readonly ILogger<Program> _logger;
private readonly ServiceA _serviceA;

public Program(ILogger<Program> logger, ServiceA serviceA)
{
_logger = logger;
_serviceA = serviceA;
}

public void Run()
{
_logger.LogInformation("Program is running.");
_serviceA.DoSomething();
_logger.LogInformation("Program is completed.");
}

static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
host.Services.GetRequiredService<Program>().Run();
}
}

This would then output our logs which is expected.

1
2
3
4
5
6
info: ConsoleAppDependencyInjection.Program[0]
Program is running.
info: ConsoleAppDependencyInjection.ServiceB[0]
Service B is doing something.
info: ConsoleAppDependencyInjection.Program[0]
Program is completed.

IServiceProvider

Required nuget packages are Microsoft.Extensions.DependencyInjection

ContainerConfiguration

Create the provider that returns IServiceProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using Microsoft.Extensions.DependencyInjection;
using System;

namespace RedisDemo
{
internal static class ContainerConfiguration
{
public static IServiceProvider Configure()
{
var services = new ServiceCollection();

// this is a convenience method from `Extensions.DependencyInjection` for distributed redis cache
services.AddDistributedRedisCache(option =>
{
option.Configuration = "localhost:6379,allowAdmin=true";
option.InstanceName = "foo-redis";
});

// add your classes to the `services` container
services.AddSingleton<ICacheRepository, CacheRepository>();

return services.BuildServiceProvider();
}
}
}

Configure Provider

Configure the provider and resolve dependencies with GetService<T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using Microsoft.Extensions.DependencyInjection;

namespace RedisDemo
{
class Program
{
static void Main(string[] args)
{
var provider = ContainerConfiguration.Configure();
var cacheRepository = provider.GetService<ICacheRepository>();

var key = "some-string-key-42";
var val = "some string value that you need fast access to!";

cacheRepository.Set(key, val);
var valFromCache = cacheRepository.Get(key);

Console.WriteLine($"REDIS: Value from cache = `{valFromCache}`");
}
}
}

App Settings

You can also create configuration options

  1. Add the nuget package Microsoft.Extensions.Configuration and Microsoft.Extensions.Configuration.Json

  2. Add appsettings.json in the root and in its properties set Copy to Output Director = Copy if newer

Example:

1
2
3
4
5
{
"LaunchDarkly": {
"SdkKey": "sdk-00000000-0000-0000-0000-000000000000"
}
}
  1. Create the ConfigurationBuilder
1
2
3
4
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appsettings.json")
.Build();
  1. Read the values by name
1
var sdkKey = configuration["LaunchDarkly:SdkKey"];

Add to DI container and use Options pattern

You can also add it to the DI container

  1. Add as singleton
1
services.AddSingleton(configuration);
  1. Create the options class
1
2
3
4
5
public class LaunchDarklyOptions
{
public const string Section = "LaunchDarkly";
public string SdkKey { get; set; }
}
  1. Resolve the values
1
2
3
var launchDarkly = configuration
.GetSection(LaunchDarklyOptions.Section)
.Get<LaunchDarklyOptions>();