Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling appsettings.json data from another class in a console app

Tags:

c#

.net-5

The following is an example code from Microsoft documentation which shows how to use appsettings.json in console application:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace ConsoleJson.Example
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using IHost host = CreateHostBuilder(args).Build();

            // Application code should start here.

            await host.RunAsync();
        }

        static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, configuration) =>
                {
                    configuration.Sources.Clear();

                    IHostEnvironment env = hostingContext.HostingEnvironment;

                    configuration
                        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);

                    IConfigurationRoot configurationRoot = configuration.Build();

                    TransientFaultHandlingOptions options = new();
                    configurationRoot.GetSection(nameof(TransientFaultHandlingOptions))
                                     .Bind(options);

                    Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
                    Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");
                });
    }
}

I need to get the configuration values in a method in another class as below. I think I will need to use Dependency injection and send IConfigurationRoot to myClass as shown in this link but can not figure out exactly how??

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace ConsoleJson.Example
{
    class Program
    {
        static void Main(string[] args)
        {
            using IHost host = CreateHostBuilder(args).Build();

            IDatabase db = xyz.GetDatabase();

            ConfigureServices.Initialize(db);
        }

        static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, configuration) =>
                {
                    configuration.Sources.Clear();

                    IHostEnvironment env = hostingContext.HostingEnvironment;

                    configuration
                        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);

                    IConfigurationRoot configurationRoot = configuration.Build();

                    TransientFaultHandlingOptions options = new();
                    configurationRoot.GetSection(nameof(TransientFaultHandlingOptions))
                                     .Bind(options);

                    
                });
    }

    class ConfigureServices
    {   public static void Initialize(IDatabase db)
        {
             Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
             Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");

        }
    }
}

So, I need to get the values from appsettings.json and use them in my Initialize method. I'm able to use the rest of the code by hardcoding the values.

I tried adding the below line after ConfigureAppConfiguration but it's not able to recognize configurationRoot

.ConfigureServices((_, services) =>services.AddSingleton<IConfigurationRoot>(configurationRoot));

All help is sincerely appreciated. Thanks

like image 209
Arnab Avatar asked May 04 '21 07:05

Arnab


People also ask

How do I add Appsettings json to the console app?

Add Json File After adding the file, right click on appsettings. json and select properties. Then set “Copy to Ouptut Directory” option to Copy Always. Add few settings to json file, so that you can verify that those settings are loaded.

How do I get Appsettings json value in startup CS?

In order to add AppSettings. json file, right click on the Project in Solution Explorer. Then click Add, then New Item and then choose App Settings File option (shown below) and click Add button. Once the File is created, it will have a DefaultConnection, below that a new AppSettings entry is added.


Video Answer


3 Answers

You can try injecting the IOptions pattern from .NET Core into your class.

Do this as follows:

Suppose your appSettings.json is as shown:

"AppSettings": {
    "AppValue": "1"
 }

Create a class to hold your configuration values:

public class AppConfiguration
{
    public AppConfiguration() { } 
    public string AppValue { get; set; } 
}

In your Startup.cs add the configuration class to the service collection as shown:

using Microsoft.Extensions.Configuration;

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<AppConfiguration>(Configuration.GetSection("AppSettings"));
    …
}

In your class, inject IOptions into the class. Assign an internal variable to the configuration. Reference the configuration data within your class and obtain settings properties:

using Microsoft.Extensions.Options;

…

public class MyService: IMyService
{
    private readonly IOptions<AppConfiguration> _appConfiguration;

    public MyService(..., IOptions<AppConfiguration> appConfiguration, …)
    {
       ...
       _appConfiguration = appConfiguration;
    }

    public SomeTask()   
    {
        // Do something with _appConfiguration.Value.AppValue; 
    }
}

This will allow your custom classes to obtain values from your appSettings.json.

Also, with your app builder, it is recommended that you application pipeline uses a startup class like this:

Host.CreateDefaultBuilder(args)
   .UseStartup<Startup>();

Then your Startup class should have the following methods:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
}

public void ConfigureServices(IServiceCollection services)
{
}

A similar skeleton startup class can be regenerated when you create a new .NET Core Web App. Try the above and let me know how it goes.

like image 191
Andrew Halil Avatar answered Nov 02 '22 15:11

Andrew Halil


I was having similar situation. I want to access AppSettings.Json custom keys into my asp.net core mvc project's controller. I am sharing console application example code which might be helpful to you.

I have used standard VS 2019 console app template with asp.net core with target as .net 5.0.

How to read AppSettings.Json (specific) key values into C# console app.


My Project file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
    <PackageReference Include="Serilog" Version="2.10.0" />
    <PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
    <PackageReference Include="Serilog.Sinks.MSSqlServer" Version="5.6.0" />
  </ItemGroup>

  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

My AppSettings.Json is:

  {
  "AppSettings": {

    "FTPLocation": "\\\\FTPHostname\\\\c$\\\\Folder\\\\ftpSite\\\\Prod\\",
    "FTPUri": "ftp://ftphostname/myFolder/",
    "CSVFileName": "TestData.csv",
    },
  "ConnectionStrings": {
    "AppDbConnString": "Server=sqlserverhost.domain.com;Database=DBName; Trusted_Connection=True; MultipleActiveResultSets=true"
    ,
"IdentityConnection": "Server=hostname.domain.com;Database=IdentityDBName; Trusted_Connection=True; MultipleActiveResultSets=true"
      },  
    "EmailAddresses": [
          "[email protected]",
          "[email protected]",
          "[email protected]"
        ],  
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      }
 }

My Program.cs is:

using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Microsoft.Extensions.Logging;
using System.IO;

namespace AppSettingsExample
{
    class Program
    {
        public static IConfigurationRoot configuration;
        static void Main(string[] args)
        {
            ServiceCollection serviceDescriptors = new ServiceCollection();

            ConfigureServices(serviceDescriptors);

            Serilog.Log.Information("Building service provider");

            IServiceProvider serviceProvider = serviceDescriptors.BuildServiceProvider();

            Console.WriteLine(configuration.GetConnectionString("AppDbConnString"));

            try
            {
                Serilog.Log.Information("start service provider");
                _ = serviceProvider.GetService<App>().Run();
                Serilog.Log.Information("end service provider");
            }
            catch (Exception ex)
            {
                Serilog.Log.Fatal(ex, "Error in building service ");
                throw;
            }
            finally
            {
                Serilog.Log.CloseAndFlush();
            }

            var connectionString = configuration.GetSection("ConnectionStrings:AppDbConnString").Value;

            Console.WriteLine(connectionString);

            var securityGroups = configuration.GetSection("ADSecurityGroups:SupervisorSecurityGroups").Value.Split(';');

            foreach (var securityGroup in securityGroups)
            {
                Console.WriteLine(securityGroup);
            }
        }
        private static void ConfigureServices(IServiceCollection serviceCollection)
        {
            serviceCollection.AddSingleton(LoggerFactory.Create(builder =>
            {
                builder.AddSerilog(dispose: true);
            }));

            serviceCollection.AddLogging();

            configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
                .AddJsonFile("appsettings.json")
                .Build();

            serviceCollection.AddSingleton<IConfigurationRoot>(configuration);

            serviceCollection.AddTransient<App>();

        }


    }
}

My App.cs is:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AppSettingsExample
{
    public class App
    {
        private readonly IConfigurationRoot _config;
        private readonly ILogger<App> _logger;

        public App(IConfigurationRoot config, ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<App>();
            _config = config;
        }

        public async Task Run()
        {
            List<string> emailAddresses = _config.GetSection("EmailAddresses").Get<List<string>>();
            foreach (string emailAddress in emailAddresses)
            {

                Console.WriteLine(emailAddress);
            }
        }
    }
}

Output of this console app:

enter image description here

like image 30
Chinmay T Avatar answered Nov 02 '22 14:11

Chinmay T


Everything here can be done by configuring the host appropriately in the console.

Since a proper representation of what you are trying to do was not provided, I will have to make some assumptions based on what was actually provided in your example.

First refactor your classes to be instances and try to avoid static coupling so that dependency injection can be used properly

public class ConfigureServices {
    private readonly TransientFaultHandlingOptions options;
    private readonly IDatabase db;
    
    public ConfigureServices(TransientFaultHandlingOptions options, IDatabase db) {
        this.options = options;
        this.db = db;
    }

    public void Initialize() {
    
         Console.WriteLine($"TransientFaultHandlingOptions.Enabled={options.Enabled}");
         Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay={options.AutoRetryDelay}");

        //... access db and options as needed here
    }
}

Review the comments below to understand how the host is being built.

class Program {
    static void Main(string[] args) {
        using IHost host = CreateHostBuilder(args).Build();

        ConfigureServices config = host.Services.GetRequiredService<ConfigureServices>();
        config.Initialize();
        
        host.Run();
    }

    static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, configuration) => {
                configuration.Sources.Clear();

                IHostEnvironment env = hostingContext.HostingEnvironment;

                configuration
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
                    
                //After this delegate has been invoked, the configuration
                //will be built and added to the HostBuilderContext
            })
            .ConfigureServices((hostingContext, services) => {
                //Get built configuration from the host builder context
                IConfiguration configuration = hostingContext.Configuration;
            
                //Extract the options from configuration.
                TransientFaultHandlingOptions options = configuration
                    .GetSection(nameof(TransientFaultHandlingOptions))
                    .Get<TransientFaultHandlingOptions>();
                    
                //And add it to services so it can be injected as needed
                services.AddSingleton<TransientFaultHandlingOptions>(_ => options);

                //Add IDatabase so it to can be injected and resolved as needed.
                services.AddScoped<IDatabase>(_ => xyz.GetDatabase());
            });
}

With all the necessary dependencies added to the host, resolve the desired service as shown above

//...

ConfigureServices config = host.Services.GetRequiredService<ConfigureServices>();
config.Initialize();

//...

There really is no need to be manually passing parameters around.

Even the manual invocation of the ConfigureServices.Initialize() could have been injected into a IHostedService and that would invoked it for you when the host is started.

public class DatabaseInitializeService: BackgroundService {
    private readonly ConfigureServices config;
    
    public DatabaseInitializeService(ConfigureServices config) {
        this.config = config;
    }
    
    protected override Task ExecuteAsync(CancellationToken stoppingToken) {
        config.Initialize();
        
        return Task.CompletedTask;
    }    
}

The hosted service would be added in ConfigureServices extension

//...

services.AddHostedService<DatabaseInitializeService>();

//...

And the Main method simplified as before.

static void Main(string[] args) {
    using IHost host = CreateHostBuilder(args).Build();

    host.Run();
}
like image 34
Nkosi Avatar answered Nov 02 '22 15:11

Nkosi