How do I configure a .NET Core 1.0.0 Console Application for Dependency Injection, Logging and Configuration?
A lot of what was written is deprecated after RC2. (see issue).
Fortunatelly there are some updated posts with excelent info:
Essential .NET - Dependency Injection with .NET Core
Essential .NET - Logging with .NET Core
I came up with the following solution. I bet there are things that can be improved, please leave comments so I can improve this answer.
In my static void Main
, I
- Setup Dependency injection
- Invoke
ConfigureServices
- Instantiate my
Application
class using DI
- Switch from 'sync Main' to 'async Application.Run()'
(It makes sense to me to switch to async as soon as possible and only once.)
On my Application
Class:
- I inject as much as possible on the class constructor.
- Catch any exception on the Run() method.
Here is the code.
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Configuration;
using System.IO;
public class Program
{
static void Main(string[] args)
{
IServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
// Application application = new Application(serviceCollection);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
var app = serviceProvider.GetService<Application>();
// For async
Task.Run(() => app.Run()).Wait(); // Exceptions thrown here will be lost! Catch them all at Run()
// Otherwise use sync as in: app.Run();
}
private static void ConfigureServices(IServiceCollection services)
{
ILoggerFactory loggerFactory = new LoggerFactory()
.AddConsole()
.AddDebug();
services.AddSingleton(loggerFactory); // Add first my already configured instance
services.AddLogging(); // Allow ILogger<T>
IConfigurationRoot configuration = GetConfiguration();
services.AddSingleton<IConfigurationRoot>(configuration);
// Support typed Options
services.AddOptions();
services.Configure<MyOptions>(configuration.GetSection("MyOptions"));
services.AddTransient<Application>();
}
private static IConfigurationRoot GetConfiguration()
{
return new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile($"appsettings.json", optional: true)
.Build();
}
}
public class MyOptions
{
public string Name { get; set; }
}
public class Application
{
ILogger _logger;
MyOptions _settings;
public Application(ILogger<Application> logger, IOptions<MyOptions> settings)
{
_logger = logger;
_settings = settings.Value;
}
public async Task Run()
{
try
{
_logger.LogInformation($"This is a console application for {_settings.Name}");
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
}
}
}
}
The AppSettings.json file:
{
"MyOptions": {
"Name" : "John"
}
}
And the project.json
file:
"dependencies": {
"Microsoft.Extensions.Configuration": "1.0.0",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
"Microsoft.Extensions.DependencyInjection": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options": "1.0.0",
"Microsoft.Extensions.PlatformAbstractions": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
On your question #2: I've read the document and unless I am missing something, it does not says you can write configuration. I'm not sure you can do that, unless you edit the JSON files manually using Newtonsoft.JSON.
If a
name/value pair is written to Configuration, it is not persisted. This
means that the written value will be lost when the sources are read
again.
For your question #3 I've included a default AppSettings.json file. Your config should have a Section where its settings match by name to the public properties of your settings class.