Using IConfiguration in C# Class Library

C#asp.net Core.Net Core

C# Problem Overview


I am building a class library using C# and Core .NET. I am trying to use configuration from a config.json file. Here are the contents of that file:

config.json

{
  "emailAddress":"[email protected]"
}

In an attempt to use config.json for my configuration, I'm referencing Microsoft.Framework.ConfigurationModel.Json in my project.json file. In my code, I have the following:

MyClass.cs

using Microsoft.Framework.ConfigurationModel;
public class MyClass
{
  public string GetEmailAddress()
  {
//    return ConfigurationManager.AppSettings["emailAddress"];  This is the approach I had been using since .NET 2.0
    return ?;  // What goes here?
  }
}

Since .NET 2.0, I had been using ConfigurationManager.AppSettings["emailAddress"]. However, I'm now trying to learn how to do it the new way via IConfiguration. My problem is, this is a class library. For that reason, I'm not sure how, or where, or when, the configuration file gets loaded. In traditional .NET, I just needed to name a file web.config for ASP.NET projects and app.config for other projects. Now, I'm not sure. I have both an ASP.NET MVC 6 project and an XUnit project. So, I'm trying to figure out how to use config.json in both of these scenarios.

Thank you!

C# Solutions


Solution 1 - C#

IMO class libraries should be agnostic to application settings data. Generally, the library consumer is the one concerned with such details. Yes, this isn't always true (e.g. if you have a class that does RSA encryption/decryption, you may want some private configuration to allow for the private key gen/storage), but for the most part, it is true.

So, in general, try to keep application settings out of the class library and have the consumer provide such data. In your comment you mention a connection string to a database. This is a perfect example of data to be kept OUT of a class library. The library shouldn't care what database it's calling to to read, just that it needs to read from one. Example below (I apologize if there's some mistakes as I am writing this on the fly from memory):

Library

Library class that uses a connection string

public class LibraryClassThatNeedsConnectionString
{
    private string connectionString;

    public LibraryClassThatNeedsConnectionString(string connectionString)
    {
        this.connectionString = connectionString;
    }

    public string ReadTheDatabase(int somePrimaryKeyIdToRead)
    {
        var result = string.Empty;

        // Read your database and set result

        return result;
    }
}

Application

appsettings.json

{
  "DatabaseSettings": {
    "ConnectionString": "MySuperCoolConnectionStringWouldGoHere"
  }
}

DatabaseSettings.cs

public class DatabaseSettings
{
    public string ConnectionString { get; set; }
}

Startup.cs

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        Configuration = new ConfigurationBuilder()
                        .SetBasePath(env.ContentRootPath)
                        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                        .AddEnvironmentVariables()
                        .Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // Setup logging
        // Configure app
            
    }

    public void ConfigureServices(IServiceCollection services)
    {
        // Configure services
        services.Configure<DatabaseSettings>(Configuration.GetSection("DatabaseSettings"));
        services.AddOptions();

        // Register our class that reads the DB into the DI framework
        services.AddTransient<IInterfaceForClass, ClassThatNeedsToReadDatabaseUsingLibrary>();
    }
}

Class that uses the library class to read the database

public interface IInterfaceForClass
{
    string ReadDatabaseUsingClassLibrary(int somePrimaryKeyIdToRead);
}

public class ClassThatNeedsToReadDatabaseUsingLibrary : IInterfaceForClass
{
    private DatabaseSettings dbSettings;
    private LibraryClassThatNeedsConnectionString libraryClassThatNeedsConnectionString;

    public ClassThatNeedsToReadDatabaseUsingLibrary(IOptions<DatabaseSettings> dbOptions)
    {
        this.dbSettings = dbOptions.Value;
        this.libraryClassThatNeedsConnectionString = new LibraryClassThatNeedsConnectionString(this.dbSettings.ConnectionString);
    }

    public string ReadDatabaseUsingClassLibrary(int somePrimaryKeyIdToRead)
    {
        return this.libraryClassThatNeedsConnectionString.ReadTheDatabase(somePrimaryKeyIdToRead);
    }
}

Some controller class that handles UI stuff to read from the DB

public class SomeController : Controller
{
    private readonly classThatReadsFromDb;

    public SomeController(IInterfaceForClass classThatReadsFromDb)
    {
        this.classThatReadsFromDb = classThatReadsFromDb;
    }

    // Controller methods
}

TL;DR

Try to avoid using application settings in a class library. Instead, have your class library be agnostic to such settings and let the consumer pass those settings in.

Edit:

I added in dependency injection into a controller class to demonstrate using dependency injection to build the class that reads from the DB. This lets the DI system resolve the necessary dependences (e.g. the DB options).

This is one way of doing it (and the best way). Another way is to inject the IOptions into the controller and manually newing up the class that reads from the DB and passing the options in (not best practice, DI is a better way to go)

Solution 2 - C#

This should work. Need to install package Microsoft.Extensions.Configuration.Json

 public static class Config
  {
    private static IConfiguration configuration;
    static Config()
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
        configuration = builder.Build();
    }
   
    public static string Get(string name)
    {
        string appSettings = configuration[name];
        return appSettings;
    }
}

Solution 3 - C#

Never used it but a quick search lead me to this...

var configuration = new Configuration();
configuration.AddJsonFile("config.json");
var emailAddress = configuration.Get("emailAddress");

Maybe you could try that.

Solution 4 - C#

First in your .csproj file add a target that hocks in the build process, see the link for more options if the following doesn't fit your needs, like publication

<Target Name="AddConfig" AfterTargets="AfterBuild">
    <Copy SourceFiles="config.json" DestinationFolder="$(OutDir)" />
</Target>

you can use it like follows

using Microsoft.Framework.ConfigurationModel;
using Microsoft.Extensions.Configuration;
using System;

public class MyClass {
    public string GetEmailAddress() {
        //For example purpose only, try to move this to a right place like configuration manager class
        string basePath= System.AppContext.BaseDirectory;
        IConfigurationRoot configuration= new ConfigurationBuilder()
            .SetBasePath(basePath)
            .AddJsonFile("config.json")
            .Build();

        return configuration.Get("emailAddress");
    }
}

Solution 5 - C#

How to read AppSettings.Json Key values into C# Controller using IConfiguration.

In case someone want to see it, for Asp.net Core .Net 5.0 example. I have gone through above answers and tweak my code little bit for my application.

If you want to see how to use this into console application visit my answer on this link, I have added example with email address as well.


My AppSettings.Json is:

{
"AppSettings": {
    "FTPLocation": "\\\\hostname\\\\c$\\\\FTPMainFolder\\\\ftpFolder\\\\Test\\",
    "FTPUri": "ftp://hostname.domainname.com/foldername/",
    "CSVFileName": "Test Load Planning.csv"  
                },
"ConnectionStrings": 
 {
 "AppDbConnString": "Server=sqlserverhostname.domainname.com;Database=DBName;Trusted_Connection=True; MultipleActiveResultSets=true"   },
 "ADSecurityGroups": { "UserSecurityGroups": "AD-DL-GROUP-NAME;AD-DL-GROUP2-NAME"},
 "Logging": 
  {
    "LogLevel": {
        "Default": "Warning"    
       }  
   }
}

My LoginController.cs is:

using Microsoft.Extensions.Configuration;
public class LoginController : BaseController
{
    
    private readonly ILoginDataServices _loginDataServices;
    private readonly IConfiguration _configuration;
    public IActionResult Index()
    {
        return View();
    }


    public LoginController(ILoginDataServices loginDataServices, IConfiguration configuration)
    {
       
            _loginDataServices = loginDataServices;
            _configuration = configuration;
        
    }


    public bool CheckLogin(string userName, string password)
    {
        if (CheckIfValidEmployee(userName))
        {
            //////checking code here....
        }
        else
        {
            return false;
        }
    }

    bool CheckIfValidEmployee(string userName)
    {

        var securityGroups = _configuration.GetSection("ADSecurityGroups:UserSecurityGroups").Value.Split(';');
         Console.WriteLine(securityGroups);
       ////////Code to check user exists into security group or not using variable value
     }

Solution 6 - C#

You can also set properties of the class library with right-click on a .csproject -> properties-> settings-> add a new property in the right window. Make sure to select access modifier as public in Access Modifier dropdown.

Now, add a class library project reference to your .net core project.

> Create appSettings.cs class as mentioned below

public class AppSettings
{
    public string MyConnectionString { get; set; }
}

> Set key-value appSettings.json

"AppSettings": {
"MyConnectionString": "yourconnectionstring",

},

> Now, we just need to get connection string from appSettings.json and > set properties into class library in Startup.cs as below.

// This method gets called by the runtime. Use this method to add services to the container
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        // inject App setting
        var appSettingSection = Configuration.GetSection("AppSettings");
        services.Configure<AppSettings>(appSettingSection);
        var appsetting = appSettingSection.Get<AppSettings>();
        // set connection string in .csproject properties.
        classLibraryProject.Properties.Settings.Default.Properties["MyConnectionString"].DefaultValue = appsetting.MyconnectionString;


    }

Note:

  • Make sure about the MyConnectionString key. It should be same in all three files.
  • Make sure to set Access modifier to Public in ClassLibrary project.

I hope this may help.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser70192View Question on Stackoverflow
Solution 1 - C#Stephen P.View Answer on Stackoverflow
Solution 2 - C#Nayas SubramanianView Answer on Stackoverflow
Solution 3 - C#chosenbreed37View Answer on Stackoverflow
Solution 4 - C#William ArdilaView Answer on Stackoverflow
Solution 5 - C#Chinmay TView Answer on Stackoverflow
Solution 6 - C#MilanView Answer on Stackoverflow