I will show how to build a production‑ready console/worker app on .NET using the Generic Host: predictable configuration (JSON + environment + user‑secrets), structured logging (console + optional Windows Event Log), dependency injection, and a background worker. A complete, runnable sample is provided.
Contents
📁 GitHub Repository: https://github.com/NickolaiA/Blog_DotNET_Console-Worker-App
Why I use the Generic Host
The .NET Generic Host unifies app startup and lifetime management across app types.
By using Host.CreateDefaultBuilder, I get configuration from JSON, per‑environment JSON,
user secrets (in Development), environment variables, and command‑line arguments — plus logging providers and DI — out of the box.
Configuration that scales from Dev to Prod
I read configuration from appsettings.json and appsettings.<Environment>.json,
overlay Development‑only values with User Secrets, and finally overlay with environment variables.
For console/worker apps I set DOTNET_ENVIRONMENT (for example Development, Staging, or Production).
# PowerShell
$env:DOTNET_ENVIRONMENT = "Development"
dotnet run
# Bash
export DOTNET_ENVIRONMENT=Development
dotnet run
For sensitive values (like SMTP password) in Development:
dotnet user-secrets init
dotnet user-secrets set "AppSettings:Smtp:Password" "your-dev-password"
Logging: console timestamps and Windows Event Log
I configure console logging with the built‑in Simple formatter and enable UTC timestamps. On Windows I optionally add the Event Log provider — if a custom SourceName is used, I ensure the source exists (creating it once requires administrator rights).
{
"Logging": {
"LogLevel": { "Default": "Information" },
"Console": {
"FormatterName": "simple",
"FormatterOptions": {
"TimestampFormat": "[yyyy-MM-ddTHH:mm:ssK] ",
"UseUtcTimestamp": true
}
},
"EventLog": {
"SourceName": "MyWorker",
"LogName": "Application"
}
}
}
Dependency Injection and options
I bind the AppSettings section to a POCO and inject via IOptions<AppSettings>.
This keeps configuration usage clean, testable, and ready for validation.
public sealed class AppSettings
{
public SmtpSettings Smtp { get; set; } = new();
public string TestRecipient { get; set; } = string.Empty;
public sealed class SmtpSettings
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; } = 587;
public bool UseSsl { get; set; } = true;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty; // user-secrets/env
public string From { get; set; } = string.Empty;
}
}
Background worker
The sample implements a BackgroundService that logs startup, optionally sends a test email,
then exits. This demonstrates clean use of ILogger<T> and IOptions<T>
in a hosted service.
public sealed class App : BackgroundService
{
private readonly ILogger<App> _logger;
private readonly IEmailService _email;
private readonly AppSettings _settings;
public App(ILogger<App> logger, IEmailService email, IOptions<AppSettings> settings)
{
_logger = logger; _email = email; _settings = settings.Value;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker starting in {{Environment}}",
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production");
if (!string.IsNullOrWhiteSpace(_settings.TestRecipient))
{
try
{
await _email.SendAsync(_settings.Smtp.From, _settings.TestRecipient,
"Hello from worker", "This is a test email sent by the worker.");
_logger.LogInformation("Test email sent to {{Recipient}}", _settings.TestRecipient);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to send test email");
}
}
else
{
_logger.LogWarning("No TestRecipient configured; skipping email send.");
}
_logger.LogInformation("Worker finished; shutting down.");
}
}
How to run the sample
First, get the sample code from GitHub:
git clone https://github.com/NickolaiA/Blog_DotNET_Console-Worker-App.git
cd Blog_DotNET_Console-Worker-App
Then follow these steps:
- Set
DOTNET_ENVIRONMENT=Development. - Set SMTP password with user‑secrets.
dotnet buildthendotnet run --project ./src/WorkerSample/WorkerSample.csproj.- (Windows) If using Event Log with a custom source, run once as Administrator to create the source.
Key source files
- Program.cs – Host setup, console logging, optional EventLog, DI registrations.
- App.cs – The
BackgroundServiceworker. - AppSettings.cs – Strongly‑typed options model.
- IEmailService + SmtpClientWrapper – Minimal SMTP abstraction for demo.
- appsettings.json + appsettings.Development.json – Logging/SMTP configuration.
- Use
Host.CreateDefaultBuilderfor consistent configuration and logging setup - Leverage user secrets for development-only sensitive values
- Implement proper structured logging with timestamps
- Use
IOptions<T>for clean configuration injection - Build testable services with proper dependency injection
References
- Sample Code Repository on GitHub – Complete working example with all the code discussed in this article
- .NET Generic Host – Microsoft Learn
- Host.CreateDefaultBuilder – defaults (config + logging)
- Configuration in .NET – sources and precedence
- User Secrets (development-only)
- Background tasks with hosted services
- BackgroundService API
- Console log formatting (Simple/Systemd/Json)
- Logging providers (EventLog specifics)
- Logging fundamentals (EventLog defaults)
- Options pattern (IOptions/IOptionsMonitor)