UPDATE: It appears that there’s now a “Worker Service” template available (thanks Hobby Developer) which scaffolds Dependency Injection, configuration and logging for you. If you wnat a long running service then that’s probably the best option but if you want an intermitantly triggered executable that does a defined task and then closes my solution is still probably preferable.
This is something that strangely doesn’t seem to be that well documented and took me a while to figure out though in the end it’s pretty simple.
All that’s required is to add the following NuGet packages and an appsettings.json file.
- Microsoft.Extensions.Configuration
- Microsoft.Extensions.Configuration.FileExtensions
- Microsoft.Extensions.Configuration.Json
- Microsoft.Extensions.DependencyInjection
The appsettings.json files “Copy to Output Directory” property should also be set to “Copy if newer” so that the application is able to access it when published.
Program.csclass Program { public static IConfigurationRoot configuration; static int Main(string[] args) { // Initialize serilog logger Log.Logger = new LoggerConfiguration() .WriteTo.Console(Serilog.Events.LogEventLevel.Debug) .MinimumLevel.Debug() .Enrich.FromLogContext() .CreateLogger(); try { // Start! MainAsync(args).Wait(); return 0; } catch { return 1; } } static async Task MainAsync(string[] args) { // Create service collection Log.Information("Creating service collection"); ServiceCollection serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); // Create service provider Log.Information("Building service provider"); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); // Print connection string to demonstrate configuration object is populated Console.WriteLine(configuration.GetConnectionString("DataConnection")); try { Log.Information("Starting service"); await serviceProvider.GetService<App>().Run(); Log.Information("Ending service"); } catch (Exception ex) { Log.Fatal(ex, "Error running service"); throw ex; } finally { Log.CloseAndFlush(); } } private static void ConfigureServices(IServiceCollection serviceCollection) { // Add logging serviceCollection.AddSingleton(LoggerFactory.Create(builder => { builder .AddSerilog(dispose: true); })); serviceCollection.AddLogging(); // Build configuration configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName) .AddJsonFile("appsettings.json", false) .Build(); // Add access to generic IConfigurationRoot serviceCollection.AddSingleton<IConfigurationRoot>(configuration); // Add app serviceCollection.AddTransient<App>(); } }
appsettings.json{ "EmailAddresses": [ "email1@test.com", "email2@test.com", "email3@test.com" ], "ConnectionStrings": { "DataConnection": "ConnectionStringToSQLDatabase" } }
An example of this in action can be seen here.
In the case of receiving the error “IConfigurationBuilder does not contain a definition for AddJsonFile” just rebuild the project and close and re-open Visual Studio.
2 Comments
jeonghwan · 5 March 2022 at 05:24
This good for me.
Ashutosh · 1 June 2022 at 07:59
You also need to install serilog nuget package.