Take the right turn to start your .NET application with dependency injection container, configuration, logging, exception handling and command line parser.
RightTurn is .NET application start-up container. The goal is to organize your application start-up code as well as provide reusable components via extensions.
Your program start-up can look like this ...
using RightTurn;
namespace RightStart
{
class Program
{
static void Main() => new Turn()
.Take<IQuickService, QuickService>((quick) => quick.Run());
}
}
instead of that...
using Microsoft.Extensions.DependencyInjection;
namespace QuickStart
{
class Program
{
static void Main()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IQuickService, QuickService>();
var serviceProvider = serviceCollection.BuildServiceProvider();
serviceProvider.GetRequiredService<IQuickService>().Run();
}
}
}
When your code grows the logic in Main
method might grow too. RightTurn will help to keep it clean and tidy.
-
Create QuickStart Console Application for .NET 5.0 or .NET Core 3.1 in Visual Studio
-
Add RightTurn.Extensions.CommandLine NuGet package.
-
Add RightTurn.Extensions.Serilog NuGet package.
-
Add following files to your project:
namespace QuickStart { interface IQuickOptions { string Name { get; } } }
using CommandLine; namespace QuickStart { class QuickOptions : IQuickOptions { [Option(HelpText = "Your name.")] public string Name { get; set; } } }
namespace QuickStart { internal interface IQuickSettings { string Message { get; } string Question { get; } } }
namespace QuickStart { class QuickSettings : IQuickSettings { public string Message { get; set; } public string Question { get; set; } } }
namespace QuickStart { interface IQuickService { void Run(); } }
using Microsoft.Extensions.Logging; using System; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; readonly IQuickOptions _options; readonly IQuickSettings _settings; public QuickService(ILogger<QuickService> logger, IQuickOptions options, IQuickSettings settings) { _logger = logger; _options = options; _settings = settings; } public void Run() { if (_options.Name is null) throw new Exception(_settings.Question); _logger.LogInformation(_settings.Message, _options.Name); } } }
Set Copy to Output Directory to Copy if newer
{ "Serilog": { "MinimumLevel": { "Default": "Information" }, "WriteTo": [ { "Name": "Console", "Args": { "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Literate, Serilog.Sinks.Console", "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u5}] {Message:lj}{NewLine}{Exception}" } } ] }, "QuickSettings": { "Message": "Hello {name}, Welcome to RightTurn.", "Question": "Who are you?" } }
-
Add start-up with RightTurn to your program's Main method.
using RightTurn; using RightTurn.Extensions.CommandLine; using RightTurn.Extensions.Configuration; using RightTurn.Extensions.Logging; using RightTurn.Extensions.Serilog; namespace QuickStart { class Program { static void Main(string[] args) => new Turn() .ParseOptions<QuickOptions>(args) .WithOptionsAsSingleton<IQuickOptions, QuickOptions>() .WithConfigurationSettings<IQuickSettings, QuickSettings>("QuickSettings") .WithSerilog() .WithUnhandledExceptionLogging() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
[2021-03-20 22:39:24 FATL] Who are you?
-
Run application with --help command line parameter.
QuickStart 1.0.0 Copyright (C) 2021 QuickStart --name Your name. --help Display this help screen. --version Display version information.
-
Run application with --name Matt command line parameter.
[2021-03-20 22:40:04 INFO] Hello Matt, Welcome to RightTurn.
See how RightTurn can help to start-up your application.
-
Create a new project from Console Application template.
-
Set Project name to
QuickStart
. -
Set Target Framework to .NET 5.0 or .NET Core 3.1.
-
Add RightTurn NuGet package.
-
Create new interface IQuickService.
namespace QuickStart { interface IQuickService { void Run(); } }
-
Create new class QuickService and implement
IQuickService
interface.using System; namespace QuickStart { class QuickService : IQuickService { public void Run() { Console.WriteLine("Hello World"); } } }
-
Add
new Turn()
to program'sMain
method.using RightTurn; namespace QuickStart { class Program { static void Main() => new Turn() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
Hello world
-
Add RightTurn.Extensions.Logging NuGet package.
-
Add
ILogger<QuickService> _logger
to QuickService class.using Microsoft.Extensions.Logging; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; public QuickService(ILogger<QuickService> logger) { _logger = logger; } public void Run() { _logger.LogInformation("Hello World"); } } }
- Add logging of your choice. See the examples below.
-
Add Microsoft.Extensions.Logging.Console NuGet package.
-
Add
WithLogging
to program'sMain
method.using RightTurn; using RightTurn.Extensions.Logging; using Microsoft.Extensions.Logging; namespace QuickStart { class Program { static void Main() => new Turn() .WithLogging((builder) => builder.AddConsole()) .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
info: QuickStart.QuickService[0] Hello World
-
Add Serilog.Extensions.Logging NuGet package.
-
Add Serilog.Sinks.Console NuGet package.
-
Add
WithLogging
to program'sMain
method.using RightTurn; using RightTurn.Extensions.Logging; using Serilog; namespace QuickStart { class Program { static void Main() => new Turn() .WithLogging((builder) => builder.AddSerilog( new LoggerConfiguration() .WriteTo.Console() .CreateLogger(), dispose: true)) .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
- Run application.
[21:50:43 INF] Hello World
-
Add RightTurn.Extensions.Serilog NuGet package.
-
Add RightTurn.Extensions.Configuration NuGet package.
-
Add
WithLogging
to program'sMain
method.using RightTurn; using RightTurn.Extensions.Serilog; using Serilog; namespace QuickStart { class Program { static void Main() => new Turn() .WithSerilog((config) => config.WriteTo.Console()) .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add New Item to your project.
-
Select JSON File and provide Name as
appsettings.json
. -
Add Serilog configuration to appsettings.json file.
{ "Serilog": { "MinimumLevel": { "Default": "Information" }, "WriteTo": [ { "Name": "Console", "Args": { "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" } } ] } }
-
Use
WithConfigurationFile()
andWithSerilog()
in program'sMain
method.using RightTurn; using RightTurn.Extensions.Serilog; namespace QuickStart { class Program { static void Main() => new Turn() .WithConfigurationFile() .WithSerilog() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add logging to file for Serilog in appsettings.json file.
{ "Serilog": { "MinimumLevel": { "Default": "Information" }, "WriteTo": [ { "Name": "Console", "Args": { "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" } }, { "Name": "File", "Args": { "path": "logs/.log", "rollingInterval": "Day" } } ] } }
- Add RightTurn.Extensions.Configuration NuGet package.
- Add New Item to your project.
- Select JSON File and provide Name as
appsettings.json
.
-
Add Logging configuration to appsettings.json file.
{ "Logging": { "LogLevel": { "Default": "Information" } } }
-
Add
WithConfigurationFile()
and configuration to logging builder inWithLogging
.using RightTurn; using RightTurn.Extensions.Logging; using Microsoft.Extensions.Logging; namespace QuickStart { class Program { static void Main() => new Turn() .WithConfigurationFile() .WithLogging((builder, turn) => builder .AddConsole() .AddConfiguration(turn.Directions.Configuration().GetSection("Logging"))) .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add "Console" section to Logging in appsettings.json file to customize console logging behaviour. For example, you can change logging level.
{ "Logging": { "LogLevel": { "Default": "Information" }, "Console": { "LogLevel": { "Default": "Warning" } } } }
-
Add Settings class to your project.
namespace QuickStart { class Settings { public string Description { get; set; } } }
-
Add "Quick" section to appsettings.json file.
{ "Logging": { "LogLevel": { "Default": "Information" } }, "Quick": { "Description": "Hello world from configuration file" } }
-
Add
.WithConfigurationSettings<Settings>("Quick")()
to main method.using RightTurn; using RightTurn.Extensions.Logging; using Microsoft.Extensions.Logging; namespace QuickStart { class Program { static void Main() => new Turn() .WithConfigurationFile() .WithLogging((builder, turn) => builder .AddConsole() .AddConfiguration(turn.Directions.Configuration().GetSection("Logging"))) .WithConfigurationSettings<Settings>("Quick") .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add
Settings
to QuickService class.using Microsoft.Extensions.Logging; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; readonly Settings _settings; public QuickService(ILogger<QuickService> logger, Settings settings) { _logger = logger; _settings = settings; } public void Run() { _logger.LogInformation(_settings.Description); } } }
-
Run application.
info: QuickStart.QuickService[0] Hello world from configuration file
-
Add ISettings interface to your project.
namespace QuickStart { interface ISettings { public string Description { get; } } }
-
Add the interface to Settings class.
namespace QuickStart { class Settings : ISettings { public string Description { get; set; } } }
-
Add
ISettings
to QuickService class.using Microsoft.Extensions.Logging; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; readonly ISettings _settings; public QuickService(ILogger<QuickService> logger, ISettings settings) { _logger = logger; _settings = settings; } public void Run() { _logger.LogInformation(_settings.Description); } } }
-
Add
.WithConfigurationSettings<ISettings, Settings>("Quick")()
to main method.using RightTurn; using RightTurn.Extensions.Logging; using Microsoft.Extensions.Logging; namespace QuickStart { class Program { static void Main() => new Turn() .WithConfigurationFile() .WithLogging((builder, turn) => builder .AddConsole() .AddConfiguration(turn.Directions.Configuration().GetSection("Logging"))) .WithConfigurationSettings<ISettings, Settings>("Quick") .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add RightTurn.Extensions.CommandLine NuGet package.
-
Add QuickOptions class to your project.
using CommandLine; namespace QuickStart { class QuickOptions { [Option(Required = true)] public string Name { get; set; } } }
-
Use
ParseOptions
to program'sMain
method.using RightTurn; using RightTurn.Extensions.CommandLine; using RightTurn.Extensions.Logging; using RightTurn.Extensions.Serilog; namespace QuickStart { class Program { static void Main(string[] args) => new Turn() .ParseOptions<QuickOptions>(args) .WithSerilog() .WithUnhandledExceptionLogging() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
QuickStart 1.0.0 Copyright (C) 2021 QuickStart ERROR(S): Required option 'name' is missing. --name Required. --help Display this help screen. --version Display version information.
-
Run application with
--name "Hello command line"
parameter.[21:39:28 INF] Hello world
The parameters were successfully parsed.
-
Add
.WithOptionsAsSingleton<QuickOptions>()
using RightTurn; using RightTurn.Extensions.CommandLine; using RightTurn.Extensions.Logging; using RightTurn.Extensions.Serilog; namespace QuickStart { class Program { static void Main(string[] args) => new Turn() .ParseOptions<QuickOptions>(args) .WithOptionsAsSingleton<QuickOptions>() .WithSerilog() .WithUnhandledExceptionLogging() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Add QuickOptions to QuickService class.
using Microsoft.Extensions.Logging; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; readonly QuickOptions _options; public QuickService(ILogger<QuickService> logger, QuickOptions options) { _logger = logger; _options = options; } public void Run() { _logger.LogInformation(_options.Name); } } }
-
Run application with
--name "Hello world from command line"
parameter.[21:44:48 INF] Hello world from command line
The example is based on the code created in Logging with RightTurn Serilog section above.
-
Add
throw new Exception
to QuickService class.using System; using Microsoft.Extensions.Logging; namespace QuickStart { class QuickService : IQuickService { readonly ILogger<QuickService> _logger; public QuickService(ILogger<QuickService> logger) { _logger = logger; } public void Run() { throw new Exception("I am unhandled exception"); } } }
-
Add
WithUnhandledExceptionHandler
toMain
method.using RightTurn; using RightTurn.Extensions.Serilog; using Serilog; namespace QuickStart { class Program { static void Main() => new Turn() .WithSerilog() .WithUnhandledExceptionHandler((exception) => Console.WriteLine(exception.Message)) .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
I am unhandled exception
-
Replace
WithUnhandledExceptionHandler
withWithUnhandledExceptionLogging
using RightTurn; using RightTurn.Extensions.Serilog; using Serilog; namespace QuickStart { class Program { static void Main() => new Turn() .WithSerilog() .WithUnhandledExceptionLogging() .Take<IQuickService, QuickService>((quick) => quick.Run()); } }
-
Run application.
[21:01:46 FTL] I am unhandled exception
The start-up Directions Container is a simple Dictionary<Type, object>
that implements ITurnDirections
interface. The interface offers access to objects stored in the container which will be referred as "directions".
To start your .NET application with turn you need to follow this pattern:
new Turn()
With
...
add services, configuration, logging, command line parser etc...
Take
...
execute your code here
In most cases you will use new Turn()
to create new start-up container.
static void Main(string[] args) => new Turn()
...
The new Turn()
creates IServiceCollection
and adds it to Directions container.
In this section you can apply available extensions and provide services and configurations required to start your program.
WithParser
Change default command line parser.
ParseVerbs
Parse command line verbs.
ParseOptions
Parse command line options.
WithDirections
Add Directions container as
ITurnDirections
singleton toIServiceCollection
.
WithServices
Add services required services. Configuration extensions provides
WithServices
extension with access toIConfiguration
.
WithTurn
Access or add directions to Directions container.
WithUnhandledExceptionLogging
Add unhandled exception handler.
WithConfiguration
Build and provide your own configuration.
WithConfigurationFile
Provide configuration from a file. Default is optional
appsettings.config
.
WithConfigurationSettings
Create settings object, binds configuration section and add the object as singleton service.
WithLogging
Provide logging configuration builder.
WithSerilog
Add Serilog logging.
The Take
will do following:
AddConfiguration
Add configuration if directions container has
ITrunConfiguration
.
AddLogging
Add logging if directions container has
ITurnLogging
.
AddServices
Add services that require access to
IConfiguration
before are added to ServiceCollection.
BuildServiceProvider
Build service provider and add
IServiceCollection
to directions container container.
if (Directions.Have<ITurnConfiguration>(out var configuration))
configuration.AddConfiguration(this);
if (Directions.Have<ITurnLogging>(out var logging))
logging.AddLogging(this);
if (Directions.Have<ITurnServices>(out var services))
services.AddServices(this);
return Directions.Add<IServiceProvider>(Directions.Get<IServiceCollection>().BuildServiceProvider());
-
RightTurn.Extensions.Configuration Provides configuration extensions.
-
RightTurn.Extensions.Logging Provides logging extensions.
-
RightTurn.Extensions.Serilog Provides Serilog extensions.
-
RightTurn.Extensions.CommandLine Provides CommandLine Parser extensions.