DEV Community

loading...
Cover image for .NET 5 Console App with Dependency Injection, Serilog Logging, and AppSettings

.NET 5 Console App with Dependency Injection, Serilog Logging, and AppSettings

moe23 profile image Mohamad Lawand ・3 min read

In this article we will be building a .Net 5 console app which support dependency injection, logging and app settings configuration.

You can watch the full Video on Youtube:

And you get the full source code on GitHub:
https://github.com/mohamadlawand087/v22-DotnetConsole

So what's in our agenda today:

  • development ingredients
  • the functionalities we are going to build
  • Coding

Development Ingredients

  • Visual Studio Code
  • Dotnet Core SDK

Functionalities:

  • Dependency Injection
  • Serilog Logger
  • AppSettings.

We are going to build a sample application which will mimic connecting to a database through dependency injection as well as outputting logs.

We will start by creating our application, inside our terminal

dotnet new console -n "SampleApp"
Enter fullscreen mode Exit fullscreen mode

Once the application has been create, open the application in Visual Studio Code and let us build and the application to make sure everything is working.

dotnet build
dotnet run
Enter fullscreen mode Exit fullscreen mode

The next step is installing the packages that we need.

dotnet add package Microsoft.Extensions.Hosting
dotnet add package Serilog.Extensions.Hosting
dotnet add package Serilog.Settings.Configuration 
dotnet add package Serilog.Sinks.Console
Enter fullscreen mode Exit fullscreen mode

The next step will be adding our appsettings.json, to do that in root directory of our application right-click select New File. Name the file appsettings.json

Inside the appsettings we are going to add all of the configuration that we need to setup serilog as well as the connectionString to mimic a database connection

{
    "Serilog" : {
        "MinimalLevel": {
            "Default": "Information",
            "Override": {
                "Microsoft": "Information",
                "System": "Warning"
            }
        }
    },
    "ConnectionStrings": {
        "DefaultConnection": "DataSource=app.db;Cache=Shared"
    }
}
Enter fullscreen mode Exit fullscreen mode

We will start by implementing the logging mechanism. Inside our Program.cs Add the following code, this code responsibility is reading the appsetting.json and making it available to our application.

static void BuildConfig(IConfigurationBuilder builder)
{
    // Check the current directory that the application is running on 
    // Then once the file 'appsetting.json' is found, we are adding it.
    // We add env variables, which can override the configs in appsettings.json
    builder.SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables();
}
Enter fullscreen mode Exit fullscreen mode

Now we need to create another method which will be out startup method for our application, it will responsible to put everything together. We will define Serilog as well our dependency injection mechanism in .Net Core.

static IHost AppStartup()
{
    var builder = new ConfigurationBuilder();
    BuildConfig(builder);

    // Specifying the configuration for serilog
    Log.Logger = new LoggerConfiguration() // initiate the logger configuration
                    .ReadFrom.Configuration(builder.Build()) // connect serilog to our configuration folder
                    .Enrich.FromLogContext() //Adds more information to our logs from built in Serilog 
                    .WriteTo.Console() // decide where the logs are going to be shown
                    .CreateLogger(); //initialise the logger

    Log.Logger.Information("Application Starting");

    var host = Host.CreateDefaultBuilder() // Initialising the Host 
                .ConfigureServices((context, services) => { // Adding the DI container for configuration

                })
                .UseSerilog() // Add Serilog
                .Build(); // Build the Host

    return host;
}
Enter fullscreen mode Exit fullscreen mode

Now let us implement data service which will mimic a database

Let us create a new class called DataService and an interface called IDataService

// Interface
public interface IDataService
{
     void Connect();
}

// Class
public class DataService : IDataService
{
    private readonly ILogger<DataService> _log;
    private readonly IConfiguration _config;
    public DataService(ILogger<DataService> log, IConfiguration config)
    {
        _log = log;
        _config = config;
    }

    public void Connect()
    {
        // Connect to the database
        var connectionString = _config.GetValue<string>("ConnectionStrings:DefaultConnection");

        _log.LogInformation("Connection String {cs}", connectionString); 
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we need to update our AppStartup method in the Program.cs class to inject the DataService

var host = Host.CreateDefaultBuilder() // Initialising the Host 
                .ConfigureServices((context, services) => { // Adding the DI container for configuration
                    **services.AddTransient<IDataService, DataService>(); // Add transiant mean give me an instance each it is being requested.**
                })
                .UseSerilog() // Add Serilog
                .Build(); // Build the Host
Enter fullscreen mode Exit fullscreen mode

And finally let us put everything together in our main method

static void Main(string[] args)
{
    var host = AppStartup();

    var service = ActivatorUtilities.CreateInstance<DataService>(host.Services);

    service.Connect();
}
Enter fullscreen mode Exit fullscreen mode

Please let me know if you want me to jump into more details about any part of this application or if there is a specific feature which you would like me to cover.

Thanks for reading

Discussion (2)

pic
Editor guide
Collapse
kvenda profile image
MyProjects

Ugh! I'm such a newbie! My little application works great and picks up all the _config values needed if I run it directly (from cmd line, thru a shortcut, or just double-clicking on the exe). But when I run it from another app (using Process), it interprets the base Directory as the directory of the calling process, and so does not pick up my _config settings from appsettings.json. (I can get the _config info for Logger, because I have the full path hard-coded into builder, but in the rest of the code, the _config data returns null. How to get past this so I can keep my appsettings separate and easily modifiable? Thanks.

Collapse
kvenda profile image
MyProjects • Edited

Thanks for a great post, and especially the video, which finally made a lot of this more understandable. FYI - I answered my own question, I needed to set the properties for appsettings.json to Copy if Newer. Orig Question: Q: how to reference the appsettings json file if it is in the project folder, not the bin/debug/net5/ folder (as with a typical scaffolded net 5 app). ?