swirl
Home Software Blog Wallpapers Webtools
Configuration support in DotNetCore
Sunday 22, November 2020   |   Post link

Overview

We'll look at the support for dealing with configurations available in .NET Core applications in this blog post. We'll use a console application to work with raw classes to understand what capabilities are available to us.

Scenario

Even a command line utility application has configuration. For example, I wrote a utility to calculate hash of file contents. The utility is run like this:

hash -f "path_to_a_file" -a [MD5|SHA1|SHA256|SHA384|SHA512] -o [base64|hex]

Now, the path to the file and the algorithm name is mandatory. The program does not run unless the user specified both. The output format can be either base64 or hex string and is optional. The program has hex output as the default harded in the program. Wouldn't it be better if the user could decide what the default output option was? This becomes part of configuration. This 'default' configuration can of course be overridden by the user by specifying the output format in the command line while ruuning the program.

Dealing with command line options

A lot of us directly parse the command line as a string array, extract options and their values and then consume those in our logic. We can save some of this effort by making use of the CommandLineConfigurationProvider class. To use this, we need to add the Microsoft.Extensions.Configuration.CommandLine package using nuget.

dotnet new console -n CmdLineConfigSample
dotnet add package Microsoft.Extensions.Configuration.CommandLine --version 5.0.0

Let's say the sample program accepts a greeting text as input and prints it on the screen. We'll see how we can use the CommandLineConfigurationProvider for this.

using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.CommandLine ;

namespace CmdLineConfigSample
{
	class Program
	{
		static void Main(string[] args)
		{
			IConfigurationProvider cmdConfig = new CommandLineConfigurationProvider(args);
			cmdConfig.Load();

			string greeting;
			if (!cmdConfig.TryGet("greeting", out greeting))
			{
				greeting = "How are you?";
			}
			
			Console.WriteLine($"{greeting}");            
		}
	}
}	

We can now pass the greeting in the following ways:

Command line configuration

This is pretty neat however, the default greeting "How are you?" has been hardcoded in our application. How about making the default configurable?

Other configuration providers

Over the years applications have used multiple formats to store configuration information. Windows 3.1, Windows 95 used ini files, these later shifted to the Windows Registry. DotNet applications use app.config XML files frequently. Today a lot of application use json. The good news is DotNetCore has support for most of these. In this post, we'll use a json file.

Create a new appsettings.json file with the following content and store in the same folder as the application source code.

{	
	"greeting": "May the force be with you!"	
}

Next we need to add a package for working with JSON configurations.

dotnet add package Microsoft.Extensions.Configuration.Json --version 5.0.0

Next we modify the .csproj file to include the appsettings.json file so it gets copied to the output folder.

<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<OutputType>Exe</OutputType>
		<TargetFramework>netcoreapp3.1</TargetFramework>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0" />
		<!-- this is what we need to add -->
		<Content Include="appsettings.json">
			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		</Content>
		<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
	</ItemGroup>
</Project>	  

The program code now looks like this:

using System;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.CommandLine ;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.FileProviders;

namespace CmdLineConfigSample
{
	class Program
	{
		private static IConfigurationProvider GetJsonConfiguration(string jsonSettingFilename)
		{            
			JsonConfigurationSource jsonSource = new JsonConfigurationSource {
				Path = jsonSettingFilename,
				Optional = false,
				FileProvider = new PhysicalFileProvider(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)),
				ReloadOnChange = true
			};

			JsonConfigurationProvider jsonSettings = new JsonConfigurationProvider(jsonSource);
			jsonSettings.Load();
			return jsonSettings;
		}

		static void Main(string[] args)
		{
			IConfigurationProvider cmdConfig = new CommandLineConfigurationProvider(args);
			cmdConfig.Load();

			IConfigurationProvider jsonConfig = GetJsonConfiguration("appsettings.json");
			string defaultGreeting = "How are you?";
			jsonConfig.TryGet("greeting", out defaultGreeting);                

			string greeting;
			if (!cmdConfig.TryGet("greeting", out greeting))
			{
				greeting = defaultGreeting;
			}
			
			Console.WriteLine($"{greeting}");            
		}
	}
}

Here is the new code in action:

Json configurations in action

This is much better. There is still room for improvement and this is possible by the use of the ChainedConfigurationProvider class.

Using multiple providers together

The ChainedConfigurationProvider class allows us to use various configuration providers together so that one provider can override the configuration provided in another provider. Who overrides whom is decided by the order of providers in the chain. This will be clear in the following sample code.

using System;
using System.IO;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.CommandLine ;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.FileProviders;

namespace CmdLineConfigSample
{
    class Program
    {   
        private static IConfigurationProvider GetChainedConfiguration(string jsonSettings, string[] args)
        {
            ConfigurationBuilder configBuilder = new ConfigurationBuilder();
            configBuilder.Add(new JsonConfigurationSource {
                Path = jsonSettings,
                Optional = false,
                FileProvider = new PhysicalFileProvider(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)),
                ReloadOnChange = true                
            });
            
            configBuilder.Add(new CommandLineConfigurationSource
            {
                Args = args
            });

            IConfigurationProvider configProvider = new ChainedConfigurationSource
            {
                Configuration = configBuilder.Build()
            }
            .Build(configBuilder);
            
            configProvider.Load();            
            return configProvider;
        }		
	
        static void Main(string[] args)
        {
            IConfigurationProvider config = GetChainedConfiguration("appsettings.json", args);
            string greeting = "How are you?";
            config.TryGet("greeting", out greeting);
            Console.WriteLine($"{greeting}");                        
		}
		
		private static IConfigurationProvider GetJsonConfiguration(string jsonSettingFilename)
        {            
            JsonConfigurationSource jsonSource = new JsonConfigurationSource {
                Path = jsonSettingFilename,
                Optional = false,
                FileProvider = new PhysicalFileProvider(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)),
                ReloadOnChange = true
            };

            JsonConfigurationProvider jsonSettings = new JsonConfigurationProvider(jsonSource);
            jsonSettings.Load();
            return jsonSettings;
        }
		
		private static void NonChained(string[] args)
        {
            IConfigurationProvider cmdConfig = new CommandLineConfigurationProvider(args);
            cmdConfig.Load();

            IConfigurationProvider jsonConfig = GetJsonConfiguration("appsettings.json");
            string defaultGreeting = "How are you?";
            jsonConfig.TryGet("greeting", out defaultGreeting);                

            string greeting;
            if (!cmdConfig.TryGet("greeting", out greeting))
            {
                greeting = defaultGreeting;
            }
            
            Console.WriteLine($"{greeting}");
        }
    }
}

The previous code has been moved into a method NonChained so we can ignore this method and the GetJsonConfiguration method completely. GetChainedConfiguration is the new method which chains together the json provider and the command line provider. Since the command line provider is added after the json provider, configurations provided by command line will override configurations provided in json.

Here how it looks in action:

Chained configuration output

References

Enjoy coding with .NET!



Categories: .NET (3)

Comments

Posts By Year

2021 (4)
2020 (12)
2019 (6)
2018 (8)
2017 (11)
2016 (6)
2015 (18)
2014 (2)
2013 (4)
2012 (2)

Posts By Category

.NET (3)
.NET Core (2)
ASP.NET MVC (4)
AWS (3)
AWS API Gateway (1)
Android (1)
Architecture (1)
Azure (2)
Book review (3)
Business (1)
C# (3)
C++ (2)
CloudHSM (1)
Containers (3)
Corporate culture (1)
Database (3)
Database migration (1)
Desktop (1)
DotNet (2)
DotNet Core (2)
Entity Framework (3)
Git (2)
IIS (1)
JDBC (1)
Java (6)
Learning (1)
Life (6)
Linux (1)
Lucene (1)
Multi-threading (1)
OData (1)
Office (1)
PHP (1)
PowerShell (2)
Programming (23)
Rants (5)
SQL (2)
SQL Server (1)
Security (1)
Software Engineering (1)
Software development (1)
Solr (1)
Sql Server (2)
Storage (1)
T-SQL (1)
TDD (1)
TSQL (5)
Tablet (1)
Technology (1)
Test Driven (1)
Unit Testing (1)
Unit Tests (1)
Utilities (2)
VC++ (1)
VMWare (1)
VSCode (1)
Visual Studio (2)
Wallpapers (1)
Web API (2)
Win32 (1)
Windows (9)
XML (2)

Posts By Tags

.NET(5) API Gateway(1) ASP.NET(4) AWS(1) Adults(1) Advertising(1) Android(1) Anti-forgery(1) Authentication(2) Azure(2) Backup(1) Beliefs(1) BlockingQueue(1) Book review(2) Books(1) Busy(1) C#(4) C++(3) CLR(1) CORS(1) CSRF(1) CTE(1) Callbacks(1) Checkbox(1) CloudHSM(1) Cmdlet(1) Commons(1) Company culture(1) Complexity(1) Consumer(1) Consumerism(1) Containers(3) Core(2) Custom(2) DPI(1) Data-time(1) Database(4) Debugging(1) Delegates(1) Developer(2) Dockers(2) DotNetCore(3) EF 1.0(1) Encrypted(1) Entity framework(1) Events(1) File copy(1) File history(1) Font(1) Git(2) GradleApache(1) HierarchyID(1) IIS(1) Installing(1) Intelli J(1) JDBC(1) JSON(1) JUnit(1) JWT(1) Java(1) JavaScript(1) LinkedIn(1) Linux(1) Localization(1) Log4J(1) Lucene(1) MVC(4) Management(2) Migration history(1) Mobile Apps(1) Modern Life(1) Money(1) NGINX(1) NTFS(1) NUnit(1) OData(1) OPENXML(1) Objects(1) Office(1) Organization(1) PHP(1) Paths(1) PowerShell(2) Producer(1) Programming(1) Python(1) Quality(1) REDIS(2) Runtimes(1) S3-Select(1) SD card(1) SQL(2) SQL Code-first Migration(1) SSH(1) Sattelite assemblies(1) School(1) Secrets Manager(1) Self reliance(1) Shell(1) Solr(1) Sony VAIO(1) Spirituality(1) Sql Express(1) System Image(1) TDD(1) TSQL(3) Table variables(1) Tables(1) Tablet(1) Url rewrite(1) VMWare(1) VSCode(1) Validation(2) Wallpaper(1) Wallpapers(1) Web Development(4) Windows(2) Windows 10(2) Windows 2016(2) Windows 8.1(1) Work culture(1) XML(1) Yii(1)