Uses of Configuration Provider and Options Pattern in ASP.NET Core

3 minute read

Application overview
In ASP.NET core, we often use application settings from different configuration files and load to a some files. Application settings can be in the appsettings.json or docker-compose.yml or .env or any other configuration file. We can use IOptions to read these settings using C#. Here is a sample code to retrieve application setting from appsettings.json, docker-compose.yml and .env file.

Tools and Technology used The following tools and technologies has been used for this application

  • Visual Studio 2019
  • Visual C#
  • ASP.NET Core Web API
  • .NET Framework 5.0

Step 1: Create a asp.net core web api project

  • Create a new project using visual studio 2019
  • Choose the C# ASP.NET Web API (.NET Core) project template
  • Create a web api project name - “Catalog.API”

Step 2: Add docker and docker compose file

  • Click right button on project -> Add Docker Support -> Linux
  • Click right button on project -> Container Orchestrator Support -> Linux

Step 3: Modify appsettings.json

  • Modify appsettings.json as follows
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },

  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=CatalogDB;Trusted_Connection=True;MultipleActiveResultSets=true",
  "ServiceName" :  "Catalog.API",
  //"Server": "LocalSQLServer",
  "Port": 112233,

  "DatabaseSettings": {
    "Server": "localhost",
    "Provider": "SQL Server",
    "Database": "DemoDb",
    "Port": 1234,
    "UserName": "sa",
    "Password": "mahedee123"
  },
  "AllowedHosts": "*"
}

Step 4: Add .env file in the root directory

  • Add .env file int root directory and modify as follows
# PORT = 4444

CONFIG_STORAGE_CATALOG_URL=http://host.docker.internal:5202/c/api/v1/catalog/items/[0]/pic/
NAME = Md. Mahedee Hasan
DBNAME = ContainerDB
PORT = 4545
#AZURE_CATALOG_DB=<YourAzureSQLDBCatalogDBConnString>

Step 5: Modify docker-compose.override.yml

  • Modify docker-compose.override.yml as follows
version: '3.4'

services:
  catalog.api:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - ConnectionString=${AZURE_CATALOG_DB:-Server=sqldata;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word}
      - PATH_BASE=/catalog-api
      - PicBaseUrl=${CONFIG_STORAGE_CATALOG_URL}
      - Name=${NAME}
      #- DbName=${DBNAME}
      - Port=${PORT}
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

Step 6: Add DatabaseSettings and CatalogSettings model class
DatabaseSettings

namespace Catalog.API
{
    public class DatabaseSettings
    {
        public string Server { get; set; }
        public string Provider { get; set; }
        public string Database { get; set; }
        public int Port { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
    }
}

CatalogSettings.cs

namespace Catalog.API
{
    public class CatalogSettings
    {
        public string ConnectionString { get; set; }
        public string ServiceName { get; set; }
        public string PicBaseUrl { get; set; }
        public string PATH_BASE { get; set; }

        public DatabaseSettings DatabaseSettings { get; set; }
    }
}

Step 7: Modify Program class
Program.cs

using Catalog.API;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;

// Using top-level programming
Console.WriteLine("Entering into Main method ... ");
var configuration = GetConfiguration();

IConfiguration GetConfiguration()
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables();

    return builder.Build();
}

try
{
    Console.WriteLine("Configuring web host {0}...", Program.AppName);
    var host = CreateHostBuilder(configuration, args);
    host.Run();

    return 0;
}
catch (Exception exp)
{
    return 1;
}
finally
{
    //finally block
}

IWebHost CreateHostBuilder(IConfiguration configuration, string[] args) =>
  WebHost.CreateDefaultBuilder(args)
      .ConfigureAppConfiguration(x => x.AddConfiguration(configuration))
      .CaptureStartupErrors(false)
      .UseStartup<Startup>()
      .Build();

public static class Program
{
    public static string Namespace = typeof(Startup).Namespace;
    public static string AppName = Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1);
}

8. Modify Startup class

Starup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

namespace Catalog.API
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers();
            // Use option pattern
            services.Configure<CatalogSettings>(Configuration);

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Catalog.API", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // Debug and Check some configuration
            var pathBase = Configuration["PATH_BASE"];
            var picBaseURL = Configuration["PicBaseUrl"];
            var name = Configuration["name"];

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Catalog.API v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Step 9: Create a Controller class for testing

  • Create Controller name CatalogController as follows CatalogController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace Catalog.API.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class CatalogController : ControllerBase
    {
        private CatalogSettings _settings;

        public CatalogController(IOptions<CatalogSettings> settings)
        {
            _settings = settings.Value;
        }
        // GET: api/<CatalogController>
        [HttpGet]
        public CatalogSettings Get()
        {
            return _settings;
        }
    }
}

Step 10: Build and run the application Now build and run the application. You will see the swagger ui and test CatalogController to test settings.

Source Code