In this blog post, I will write about Dependency Injection in .NET Core Console Application. I will start from where I left in my last blog https://dotnetcorecentral.com/blog/docker-running-background-thread/.
What is Dependency Injection?
In Dependency Injection, dependencies of an object are sent from outside. Meaning, the dependencies of an object are not created inside of the class.
All the dependency injection packages support injecting dependencies through constructor of the class. And that’s the preferable way of using dependency. Some packages support dependency through a property as well. But I personally prefer constructor injection, as it prevents the object from being mutated.
Refactoring the code
Firstly, I will refactor the existing Main
method to have the implementation across multiple classes. Each class having its own responsibility. Below is the implementation of the Main
method:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Docker.Demo
{
class Program
{
private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("Hello Docker World!");
var count = 0;
Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine($"Current Count {++count}");
Thread.Sleep(1000);
}
});
Console.WriteLine("Press Ctrl + C to cancel!");
Console.CancelKeyPress += ((s, a) =>
{
Console.WriteLine("Bye!");
_closingEvent.Set();
});
_closingEvent.WaitOne();
}
}
}
Extracting the task
Firstly, I will extract the whole code into a new class named ContinuousRunningProcessor
. The responsibility of the Program
class will be just to instantiate ContinuousRunningProcessor
and start it. But for the time being, we will keep Program
class’s Main
method to have only one Console.WriteLine method.
using System;
namespace Docker.Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Docker World!");
}
}
}
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Docker.Demo
{
internal class ContinuousRunningProcessor
{
private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false);
public void Process()
{
var count = 0;
Task.Factory.StartNew(() =>
{
while (true)
{
Console.WriteLine($"Current Count {++count}");
Thread.Sleep(1000);
}
});
Console.WriteLine("Press Ctrl + C to cancel!");
Console.CancelKeyPress += ((s, a) =>
{
Console.WriteLine("Bye!");
_closingEvent.Set();
});
_closingEvent.WaitOne();
}
}
}
Refactoring ContinuousRunningProcessor
Secondly, I will refactor ContinuousRunningProcessor
to give the responsibility of printing to ConsolePrinter
.
using System;
namespace Docker.Demo
{
internal interface IConsolePrinter
{
void Print(int count);
}
internal class ConsolePrinter : IConsolePrinter
{
public void Print(int count)
{
Console.WriteLine($"Current Count {count}");
}
}
}
Finally, I will introduce a class PrintSettingsProvider
, whose responsibility is to provide a setting to ConsolePrinter
to determine if it can print.
using System;
using System.Collections.Generic;
using System.Text;
namespace Docker.Demo
{
internal interface IPrintSettingsProvider
{
bool CanPrint();
}
internal class PrintSettingsProvider : IPrintSettingsProvider
{
public bool CanPrint()
{
return true;
}
}
}
using System;
namespace Docker.Demo
{
internal interface IConsolePrinter
{
void Print(int count);
}
internal class ConsolePrinter : IConsolePrinter
{
private readonly IPrintSettingsProvider printSettingsProvider;
public ConsolePrinter(IPrintSettingsProvider printSettingsProvider)
{
this.printSettingsProvider = printSettingsProvider;
}
public void Print(int count)
{
if (printSettingsProvider.CanPrint())
{
Console.WriteLine($"Current Count {count}");
}
}
}
}
The final version of ContinuousRunningProcessor
Moreover, now I will refactor ContinuousRunningProcessor
to introduce IConsolePrinter
to print. In addition, I will add a ILogger
to log the messages. Now to do that I will add the NuGet package Microsoft.Extensions.Logging
and Microsoft.Extensions.Logging.Console
. The package Microsoft.Extensions.Logging.Console
to log in the console.
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Docker.Demo
{
internal class ContinuousRunningProcessor
{
private static readonly AutoResetEvent _closingEvent = new AutoResetEvent(false);
private readonly IConsolePrinter consolePrinter;
private readonly ILogger<ContinuousRunningProcessor> logger;
public ContinuousRunningProcessor(IConsolePrinter consolePrinter, ILogger<ContinuousRunningProcessor> logger)
{
this.consolePrinter = consolePrinter;
this.logger = logger;
}
public void Process()
{
var count = 0;
Task.Factory.StartNew(() =>
{
logger.LogInformation("Process started!");
while (true)
{
consolePrinter.Print(++count);
Thread.Sleep(1000);
}
});
Console.WriteLine("Press Ctrl + C to cancel!");
Console.CancelKeyPress += ((s, a) =>
{
Console.WriteLine("Bye!");
_closingEvent.Set();
});
_closingEvent.WaitOne();
}
}
}
Adding Dependency Injection registration
Now that all the refactoring is done; therefore it is time to add .Net Core DI framework to the code. Firstly, I will install the Dependency Injection NuGet package Microsoft.Extensions.DependencyInjection
.
Secondly, once the package is added, I will create a class ContainerConfiguration
to register for all the classes.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Docker.Demo
{
internal static class ContainerConfiguration
{
public static ServiceProvider Configure()
{
return new ServiceCollection()
.AddLogging(l => l.AddConsole())
.Configure<LoggerFilterOptions>(c => c.MinLevel = LogLevel.Trace)
.AddSingleton<IPrintSettingsProvider, PrintSettingsProvider>()
.AddSingleton<IConsolePrinter, ConsolePrinter>()
.AddSingleton<ContinuousRunningProcessor>()
.BuildServiceProvider();
}
}
}
Refactoring Main method
Finally, I will refactor the main method and get the reference of the ContinuousRunningProcessor
from the DI container and call the Process
method.
using System;
using Microsoft.Extensions.DependencyInjection;
namespace Docker.Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Docker World!");
var serviceProvider = ContainerConfiguration.Configure();
serviceProvider.GetService<ContinuousRunningProcessor>().Process();
}
}
}
Finally, I will run the application.
Conclusion
In conclusion, this blog I walked through how to use DI container in a .Net Core console application.
Above all, the code is available in: http://github.com/choudhurynirjhar/docker-demo
In addition to the code, below is the YouTube video. https://youtu.be/2TgWRfOnOc0