Using IConfiguration in C# Class Library - c#

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":"someone#somewhere.com"
}
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!

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)

Supports both appSettings.json and appSettings.Development.json:
Config class implementation:
using Microsoft.Extensions.Configuration;
using System.IO;
public static class Config
{
private static IConfiguration configuration;
static Config()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("appSettings.Development.json", optional: true, reloadOnChange: true);
configuration = builder.Build();
}
public static string Get(string name)
{
string appSettings = configuration[name];
return appSettings;
}
public static IConfigurationSection GetSection(string name)
{
return configuration.GetSection(name);
}
}
Config class usage:
Section
var cosmosDb = new CosmosDbProviderConfiguration();
Config.GetSection(CosmosDbProviderConfiguration.CosmosDbProvider).Bind(cosmosDb);
Key
var email = Config.Get("no-reply-email");

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.

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");
}
}

In .NET 6.0+ This was the solution I found for getting the connectionString for entity framework. There were some issues finding the correct nuget package (Microsoft.Extensions.Configuration.Json). Hopefully this saves everyone some trouble.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
//nuget package: Microsoft.Extensions.Configuration.Json
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var builder = new ConfigurationBuilder();
builder.AddJsonFile(path);
var root = builder.Build();
var connectionString = root.GetSection("ConnectionStrings").GetSection("DefaultConnection").Value;
optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
}
}

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.

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
}

Related

Dependency Injection how to re-add all services to builder.Services after config change

This question is specific to NET Maui in my case.
My Settings file:
{
"MyConfig": {
"FactoryConfig1": {
"SomeType1": "SomeConfig1"
},
"FactoryConfig2": {
"SomeType2": "SomeConfig2"
}
}
}
Let's say I'm creating my services like this:
var builder = MauiApp.CreateBuilder();
builder.Services.AddSingleton<IService1, Service1>();
builder.Services.AddSingleton<IService2, Service2>();
builder.Services.AddTransient<Service3>();
// and config file-- how to modify this to use IOptions to use with IOptions Monitor?
var a = Assembly.GetExecutingAssembly();
using var stream = a.GetManifestResourceStream("MauiBlazorTestApp.appsettings.json");
var config = new ConfigurationBuilder()
.AddJsonStream(stream)
.Build();
builder.Configuration.AddConfiguration(config);
return builder.Build();
then in another class I get the config:
public class Service1 : IService1
{
IConfiguration _configuration;
public Service1(IConfiguration configuration)
{
_configuration = configuration;
}
public void SomeMethod()
{
var myConfig = _configuration.GetRequiredSection("MyConfig").Get<MyConfig>();
}
}
In each of the services, I'm injecting an IConfiguration with the configuration parameters used in each service from an appsettings.json file.
The code above works the way it is but what if sometime after starting the app, and after the services have been already created with the existing settings, the user changes some settings in appsettings.json from a UI dashboard page, how can I reload all those services with the new config settings for each...
I have to clear the services, and re-add them, is this possible, in a class outside program.cs?
It would work if I restart the application, but can it be done without, just a refresh of the DI?
As #Nkosi mentioned in the comments you should always use option pattern and not directly access the IConfiguration object.
In your case just use a class to store the desired values from configuration:
public class MyConfig
{
// The class to bind IConfiguration to
public string YourSetting { get; set; }
}
add options to the service collection and configure them. Also note the reloadOnChange parameter used during setup which indicates whether the configuration is reloaded if the file changes.
//...
builder.Configuration
.AddJsonFile("MauiBlazorTestApp.appsettings.json", optional: false, reloadOnChange: true);
// Add and configure your options
builder.Services.AddOptions();
builder.Services.Configure<MyConfig>(builder.Configuration.GetRequiredSection("MyConfig"));
//...
and finally inject the options into the target services and access the settings as needed.
public class Service1 : IService1
{
private readonly IOptionsMonitor<MyConfig> _options;
public Service1(IOptionsMonitor<MyConfig> options)
{
_options = options;
}
public void SomeMethod()
{
// Read the current value provided via IConfiguration
MyConfig appSettings = _options.CurrentValue;
var value = appSettings.YourSetting;
//...use setting as needed
}
}
When ever the current setting is requested, it will pull the latest version from settings, even if the service is a singeton.
For more information you can find this pattern very well documented here and especially for the IOptionsMonitor here

Read appsettings json values in .NET Core Test Project

My Web application needs to read the Document DB keys from appsettings.json file. I have created a class with the key names and reading the Config section in ConfigureServices() as:
public Startup(IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services.AddSession();
Helpers.GetConfigurationSettings(services, Configuration);
DIBuilder.AddDependency(services, Configuration);
}
I'm looking for the ways to read the Key values in Test project.
This is based on the blog post Using Configuration files in .NET Core Unit Test Projects (written for .NET Core 1.0).
Create (or copy) the appsettings.test.json in the Integration test project root directory, and in properties specify "Build Action" as Content and "Copy if newer" to Output Directory. Note that it’s better to have file name (e.g. appsettings.test.json ) different from normal appsettings.json, because it is possible that a file from the main project override the file from the test project, if the same name will be used.
Include the JSON Configuration file NuGet package (Microsoft.Extensions.Configuration.Json) if it's not included yet.
In the test project create a method,
public static IConfiguration InitConfiguration()
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.test.json")
.AddEnvironmentVariables()
.Build();
return config;
}
AddEnvironmentVariables (suggested in #RickStrahl blog ) is useful if you want to pass some secrets, that you prefer not store in appsettings.test.json
Use the configuration as usual
var config = InitConfiguration();
var clientId = config["CLIENT_ID"]
BTW: You also may be interesting in reading the configuration into the IOptions class as described in Integration test with IOptions<> in .NET Core:
var options = config.Get<MySettings>();
Add the configuration file
First, add a appconfig.json file to the Integration test project
Configure the appconfig.json file to be copied to the output
directory by updating
Add NuGet package
Microsoft.Extensions.Configuration.Json
Use the configuration in your unit tests
[TestClass]
public class IntegrationTests
{
public IntegrationTests()
{
var config = new ConfigurationBuilder().AddJsonFile("appconfig.json").Build();
_numberOfPumps = Convert.ToInt32(config["NumberOfPumps"]);
_numberOfMessages = Convert.ToInt32(config["NumberOfMessages"]);
_databaseUrl = config["DatabaseUrlAddress"];
}
}
Suderson's solution worked for me when modified as below:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
IConfiguration config = builder.Build();
//Now, You can use config.GetSection(key) to get the config entries
For ASP.NET Core 2.x projects, copy the appsettings.json file to the build dir automatically:
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<None Include="..\MyProj\appsettings.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>
Similar to Artem answer, but using an embedded resource (as stream):
Stream configStream =
Assembly.GetExecutingAssembly()
.GetManifestResourceStream("MyNamespace.AppName.Test.appsettings.test.json");
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonStream(configStream)
.AddEnvironmentVariables()
.Build();
Copy the appSettings.json to your Test project root directory and mark its property as Content and Copy if newer.
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
ConfigurationManager.Configuration = builder.Build();
ConfigurationManager is a class and it has a static property Configuration. This way the whole application can just access it as ConfigurationManager.Configuration[<key>]
I prefer to read configuration from a stream rather than from a file. That gives more flexibility because you can create a light weight test setup without committing multiple json configuration files:
public static class ConfigurationHelper
{
public static IConfigurationRoot GetConfiguration()
{
byte[] byteArray = Encoding.ASCII.GetBytes("{\"Root\":{\"Section\": { ... }}");
using var stream = new MemoryStream(byteArray);
return new ConfigurationBuilder()
.AddJsonStream(stream)
.Build();
}
}
In the project.json from you test project, add the following dependencies:
"dependencies": {
"xunit": "2.2.0-beta2-build3300",
"Microsoft.AspNetCore.TestHost": "1.0.0",
"dotnet-test-xunit": "2.2.0-preview2-build1029",
"BancoSentencas": "1.0.0-*"
},
BancoSentencas is the project I want to test. The other packages are from xUnit and the TestHost that will be our in-memory server.
Include also this build option for the appsettings.json:
"buildOptions": {
"copyToOutput": {
"include": [ "appsettings.Development.json" ]
}
}
In my test project, I have the following test class:
public class ClasseControllerTeste : IClassFixture<TestServerFixture> {
public ClasseControllerTeste(TestServerFixture fixture) {
Fixture = fixture;
}
protected TestServerFixture Fixture { get; private set; }
[Fact]
public async void TestarRecuperarClassePorId() {
using(var client = Fixture.Client) {
var request = await Fixture.MyHttpRequestMessage(HttpMethod.Get, "/api/classe/1436");
var response = await client.SendAsync(request);
string obj = await response.Content.ReadAsStringAsync();
ClasseModel classe = JsonConvert.DeserializeObject<ClasseModel>(obj);
Assert.NotNull(classe);
Assert.Equal(1436, classe.Id);
}
}
}
And I also have the TestServerFixture class that will configure the in-memory server:
public class TestServerFixture : IDisposable {
private TestServer testServer;
protected TestServer TestServer {
get {
if (testServer == null)
testServer = new TestServer(new WebHostBuilder().UseEnvironment("Development").UseStartup<Startup>());
return testServer;
}
}
protected SetCookieHeaderValue Cookie { get; set; }
public HttpClient Client {
get {
return TestServer.CreateClient();
}
}
public async Task<HttpRequestMessage> MyHttpRequestMessage(HttpMethod method, string requestUri) {
...
login stuff...
...
Cookie = SetCookieHeaderValue.Parse(response.Headers.GetValues("Set-Cookie").First());
var request = new HttpRequestMessage(method, requestUri);
request.Headers.Add("Cookie", new CookieHeaderValue(Cookie.Name, Cookie.Value).ToString());
request.Headers.Accept.ParseAdd("text/xml");
request.Headers.AcceptCharset.ParseAdd("utf-8");
return request;
}
public void Dispose() {
if (testServer != null) {
testServer.Dispose();
testServer = null;
}
}
}
That's how I test my project. I use the Startup.cs from the main project, and I create a copy from the appsettings.json in my test project (appsettings.Development.json)
If you are using WebApplicationFactory to create a test server for integration tests and you already have a way to get at config values in your server-side controllers (you probably do!), then you can just re-use this (and get at any other injected items you need) in your integration tests, as follows:
// Your test fixtures would be subclasses of this
public class IntegrationTestBase : IDisposable
{
private readonly WebApplicationFactory<Startup> _factory;
protected readonly HttpClient _client;
// The same config class which would be injected into your server-side controllers
protected readonly IMyConfigService _myConfigService;
// Constructor (called by subclasses)
protected IntegrationTestBase()
{
// this can refer to the actual live Startup class!
_factory = new WebApplicationFactory<Startup>();
_client = _factory.CreateClient();
// fetch some useful objects from the injection service
_myConfigService = (IMyConfigService)_factory.Server.Host.Services.GetService(typeof(IMyConfigService));
}
public virtual void Dispose()
{
_client.Dispose();
_factory.Dispose();
}
}
Note that you do not need to copy over appsettings.json in this case, you're automatically using the same appsettings.json which the (test) server is using.
Honestly, if you are unit testing an application, you should try to isolate the class you are testing from all dependencies, like calling other classes, accessing file system, database, network etc. Unless you are doing integration testing or functional testing.
Having that said, to unit test the application, you probably want to mock these values from your appsettings.json file, and just test your logic.
So your appsettings.json would look like this.
"DocumentDb": {
"Key": "key1"
}
Then create a settings class.
public class DocumentDbSettings
{
public string Key { get; set; }
}
Then register it in ConfigureServices() method.
services.Configure<DocumentDbSettings>(Configuration.GetSection("DocumentDb"));
Then for example your controller/class could look like this.
// ...
private readonly DocumentDbSettings _settings;
public HomeController(IOptions<DocumentDbSettings> settings)
{
_settings = settings.Value;
}
// ...
public string TestMe()
{
return $"processed_{_settings.Key}";
}
Then in your tests project you can create such unit test class.
public class HomeControllerTests
{
[Fact]
public void TestMe_KeyShouldBeEqual_WhenKeyIsKey1()
{
// Arrange
const string expectedValue = "processed_key1";
var configMock = Substitute.For<IOptions<DocumentDbSettings>>();
configMock.Value.Returns(new DocumentDbSettings
{
Key = "key1" // Mocking the value from your config
});
var c = new HomeController(configMock);
// Act
var result = c.TestMe();
// Assert
Assert.Equal(expectedValue, result);
}
}
I used NSubstitute v2.0.0-rc for mocking.

How do I access Configuration in any class in ASP.NET Core?

I have gone through configuration documentation on ASP.NET core. Documentation says you can access configuration from anywhere in the application.
Below is Startup.cs created by template
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
}
}
So in Startup.cs we configure all the settings, Startup.cs also has a property named Configuration
What I'm not able to understand how do you access this configuration in controller or anywhere in the application? MS is recommending to use options pattern but I have only 4-5 key-value pairs so I would like not to use options pattern. I just wanted to have access to Configuration in application. How do I inject it in any class?
Update
Using ASP.NET Core 2.0 will automatically add the IConfiguration instance of your application in the dependency injection container. This also works in conjunction with ConfigureAppConfiguration on the WebHostBuilder.
For example:
public static void Main(string[] args)
{
var host = WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder =>
{
builder.AddIniFile("foo.ini");
})
.UseStartup<Startup>()
.Build();
host.Run();
}
It's just as easy as adding the IConfiguration instance to the service collection as a singleton object in ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
// ...
}
Where Configuration is the instance in your Startup class.
This allows you to inject IConfiguration in any controller or service:
public class HomeController
{
public HomeController(IConfiguration configuration)
{
// Use IConfiguration instance
}
}
The right way to do it:
In .NET Core you can inject the IConfiguration as a parameter into your Class constructor, and it will be available.
public class MyClass
{
private IConfiguration configuration;
public MyClass(IConfiguration configuration)
{
ConnectionString = new configuration.GetValue<string>("ConnectionString");
}
Now, when you want to create an instance of your class, since your class gets injected the IConfiguration, you won't be able to just do new MyClass(), because it needs a IConfiguration parameter injected into the constructor, so, you will need to inject your class as well to the injecting chain, which means two simple steps:
1) Add your Class/es - where you want to use the IConfiguration, to the IServiceCollection at the ConfigureServices() method in Startup.cs
services.AddTransient<MyClass>();
2) Define an instance - let's say in the Controller, and inject it using the constructor:
public class MyController : ControllerBase
{
private MyClass _myClass;
public MyController(MyClass myClass)
{
_myClass = myClass;
}
Now you should be able to enjoy your _myClass.configuration freely...
Another option:
If you are still looking for a way to have it available without having to inject the classes into the controller, then you can store it in a static class, which you will configure in the Startup.cs, something like:
public static class MyAppData
{
public static IConfiguration Configuration;
}
And your Startup constructor should look like this:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
MyAppData.Configuration = configuration;
}
Then use MyAppData.Configuration anywhere in your program.
Don't confront me why the first option is the right way, I can just see experienced developers always avoid garbage data along their way, and it's well understood that it's not the best practice to have loads of data available in memory all the time, neither is it good for performance and nor for development, and perhaps it's also more secure to only have with you what you need.
I know this is old but given the IOptions patterns is relatively simple to implement:
Class with public get/set properties that match the settings in the configuration
public class ApplicationSettings
{
public string UrlBasePath { get; set; }
}
register your settings
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
...
}
inject via IOptions
public class HomeController
{
public HomeController(IOptions<ApplicationSettings> appSettings)
{ ...
appSettings.Value.UrlBasePath
...
// or better practice create a readonly private reference
}
}
I'm not sure why you wouldn't just do this.
There is also an option to make configuration static in startup.cs so that what you can access it anywhere with ease, static variables are convenient huh!
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
internal static IConfiguration Configuration { get; private set; }
This makes configuration accessible anywhere using Startup.Configuration.GetSection... What can go wrong?
I'm doing it like this at the moment:
// Requires NuGet package Microsoft.Extensions.Configuration.Json
using Microsoft.Extensions.Configuration;
using System.IO;
namespace ImagesToMssql.AppsettingsJson
{
public static class AppSettingsJson
{
public static IConfigurationRoot GetAppSettings()
{
string applicationExeDirectory = ApplicationExeDirectory();
var builder = new ConfigurationBuilder()
.SetBasePath(applicationExeDirectory)
.AddJsonFile("appsettings.json");
return builder.Build();
}
private static string ApplicationExeDirectory()
{
var location = System.Reflection.Assembly.GetExecutingAssembly().Location;
var appRoot = Path.GetDirectoryName(location);
return appRoot;
}
}
}
And then I use this where I need to get the data from the appsettings.json file:
var appSettingsJson = AppSettingsJson.GetAppSettings();
// appSettingsJson["keyName"]
I know there may be several ways to do this, I'm using Core 3.1 and was looking for the optimal/cleaner option and I ended up doing this:
My startup class is as default
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();
}
My appsettings.json is like this
{
"CompanySettings": {
"name": "Fake Co"
}
}
My class is an API Controller, so first I added the using reference and then injected the IConfiguration interface
using Microsoft.Extensions.Configuration;
public class EmployeeController
{
private IConfiguration _configuration;
public EmployeeController(IConfiguration configuration)
{
_configuration = configuration;
}
}
Finally I used the GetValue method
public async Task<IActionResult> Post([FromBody] EmployeeModel form)
{
var companyName = configuration.GetValue<string>("CompanySettings:name");
// companyName = "Fake Co"
}
I looked into the options pattern sample and saw this:
public class Startup
{
public Startup(IConfiguration config)
{
// Configuration from appsettings.json has already been loaded by
// CreateDefaultBuilder on WebHost in Program.cs. Use DI to load
// the configuration into the Configuration property.
Configuration = config;
}
...
}
When adding Iconfiguration in the constructor of my class, I could access the configuration options through DI.
Example:
public class MyClass{
private Iconfiguration _config;
public MyClass(Iconfiguration config){
_config = config;
}
... // access _config["myAppSetting"] anywhere in this class
}
In 8-2017 Microsoft came out with System.Configuration for .NET CORE v4.4. Currently v4.5 and v4.6 preview.
For those of us, who works on transformation from .Net Framework to CORE, this is essential. It allows to keep and use current app.config files, which can be accessed from any assembly. It is probably even can be an alternative to appsettings.json, since Microsoft realized the need for it. It works same as before in FW. There is one difference:
In the web applications, [e.g. ASP.NET CORE WEB API] you need to use app.config and not web.config for your appSettings or configurationSection. You might need to use web.config but only if you deploying your site via IIS. You place IIS-specific settings into web.config
I've tested it with netstandard20 DLL and Asp.net Core Web Api and it is all working.
Using the Options pattern in ASP.NET Core is the way to go. I just want to add, if you need to access the options within your startup.cs, I recommend to do it this way:
CosmosDbOptions.cs:
public class CosmosDbOptions
{
public string ConnectionString { get; set; }
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// This is how you can access the Connection String:
var connectionString = Configuration.GetSection(nameof(CosmosDbOptions))[nameof(CosmosDbOptions.ConnectionString)];
}
I have to read own parameters by startup.
That has to be there before the WebHost is started (as I need the “to listen” url/IP and port from the parameter file and apply it to the WebHost). Further, I need the settings public in the whole application.
After searching for a while (no complete example found, only snippets) and after various try-and-error's, I have decided to do it the “old way" with an own .ini file.
So.. if you want to use your own .ini file and/or set the "to listen url/IP" your own and/or need the settings public, this is for you...
Complete example, valid for core 2.1 (mvc):
Create an .ini-file - example:
[Startup]
URL=http://172.16.1.201:22222
[Parameter]
*Dummy1=gew7623
Dummy1=true
Dummy2=1
whereby the Dummyx are only included as example for other date types than string (and also to test the case “wrong param” (see code below).
Added a code file in the root of the project, to store the global variables:
namespace MatrixGuide
{
public static class GV
{
// In this class all gobals are defined
static string _cURL;
public static string cURL // URL (IP + Port) on that the application has to listen
{
get { return _cURL; }
set { _cURL = value; }
}
static bool _bdummy1;
public static bool bdummy1 //
{
get { return _bdummy1; }
set { _bdummy1 = value; }
}
static int _idummy1;
public static int idummy1 //
{
get { return _idummy1; }
set { _idummy1 = value; }
}
static bool _bFehler_Ini;
public static bool bFehler_Ini //
{
get { return _bFehler_Ini; }
set { _bFehler_Ini = value; }
}
// add further GV variables here..
}
// Add further classes here...
}
Changed the code in program.cs (before CreateWebHostBuilder()):
namespace MatrixGuide
{
public class Program
{
public static void Main(string[] args)
{
// Read .ini file and overtake the contend in globale
// Do it in an try-catch to be able to react to errors
GV.bFehler_Ini = false;
try
{
var iniconfig = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddIniFile("matrixGuide.ini", optional: false, reloadOnChange: true)
.Build();
string cURL = iniconfig.GetValue<string>("Startup:URL");
bool bdummy1 = iniconfig.GetValue<bool>("Parameter:Dummy1");
int idummy2 = iniconfig.GetValue<int>("Parameter:Dummy2");
//
GV.cURL = cURL;
GV.bdummy1 = bdummy1;
GV.idummy1 = idummy2;
}
catch (Exception e)
{
GV.bFehler_Ini = true;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("!! Fehler beim Lesen von MatrixGuide.ini !!");
Console.WriteLine("Message:" + e.Message);
if (!(e.InnerException != null))
{
Console.WriteLine("InnerException: " + e.InnerException.ToString());
}
Console.ForegroundColor = ConsoleColor.White;
}
// End .ini file processing
//
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>() //;
.UseUrls(GV.cURL, "http://localhost:5000"); // set the to use URL from .ini -> no impact to IISExpress
}
}
This way:
My Application config is separated from the appsettings.json and I
have no sideeffects to fear, if MS does changes in future versions ;-)
I have my settings in global variables
I am able to set the "to listen url" for each device, the applicaton run's on (my dev machine, the intranet server and the internet server)
I'm able to deactivate settings, the old way (just set a * before)
I'm able to react, if something is wrong in the .ini file (e.g. type mismatch)
If - e.g. - a wrong type is set (e.g. the *Dummy1=gew7623 is activated instead of
the Dummy1=true) the host shows red information's on the console
(including the exception) and I' able to react also in the
application (GV.bFehler_Ini ist set to true, if there are errors with
the .ini)

Passing application's connection string down to a Repository Class Library in ASP.NET 5 using the IConfigurationRoot

I have an ASP.NET 5 MVC Web Application and in Startup.cs I see that the public property
IConfigurationRoot Configuration
is being set to
builder.Build();
Throughout the MVC Web Application I can simply do
Startup.Configuration["Data:DefaultConnection:ConnectionString"]
to get the conn string from the appsettings.json file.
How can I get the connection string specified in the ASP.NET 5 MVC appsettings.json passed down to my Repository Class Library using constructor injection?
UPDATE:
Here is the base repository that all other repositories inherit from (as you can see I have a hardcoded connection string in here for now):
public class BaseRepo
{
public static string ConnectionString = "Server=MYSERVER;Database=MYDATABASE;Trusted_Connection=True;";
public static SqlConnection GetOpenConnection()
{
var cs = ConnectionString;
var connection = new SqlConnection(cs);
connection.Open();
return connection;
}
}
In my asp.net 5 web application in my appsettings.json file I have the following which is equivalent to adding a connection string to a web.config in a .net 4.5 webapp:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=MYSERVER;Database=MYDATABASE;Trusted_Connection=True;"
}
}
Additionally in my asp.net 5 web application I have the following default code in my Startup.cs which loads the sites configuration into a public property of type IConfigurationRoot:
public IConfigurationRoot Configuration { get; set; }
// Class Constructor
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
Now in my asp.net web application if I would like to access any of the appsettings I can simple do the following: Startup.Configuration["Data:DefaultConnection:ConnectionString"]
But unfortunately I can't do this from my class library..
If someone wants to try and figure this out here are the steps to reproduce:
Create a new ASP.NET 5 MVC Web App.
Add another project of type Class Library (Package) to the project.
Figure out a way to pass appsettings from the ASP.NET 5 MVC App to the Class Library
After updating I still can't quite get it. Here is my code:
public class BaseRepo
{
private readonly IConfigurationRoot config;
public BaseRepo(IConfigurationRoot config)
{
this.config = config;
}
}
This class declaration does not work since BaseRepo requires a constructor param now.
public class CustomerRepo : BaseRepository, ICustomerRepo
{
public Customer Find(int id)
{
using (var connection = GetOpenConnection())
{
...
}
}
}
on your Startup.cs file add the following method
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton(_ => Configuration);
}
then update your BaseRepo class like this
public class BaseRepo {
private readonly IConfiguration config;
public BaseRepo(IConfiguration config) {
this.config = config;
}
public SqlConnection GetOpenConnection() {
string cs = config["Data:DefaultConnection:ConnectionString"];
SqlConnection connection = new SqlConnection(cs);
connection.Open();
return connection;
}
}
ASP.NET provides its own way of passing around configuration settings.
Suppose you have the this in your appSettings.json:
{
"Config": {
"Setting1": 1,
"Setting2": "SO"
}
}
Then you need a class like this:
public class MyConfiguration
{
public int Setting1 { get; set; }
public string Setting2 { get; set; }
}
This allows you to configure your service with this configuration by adding the following line
services.Configure<MyConfigurationDto>(Configuration.GetSection("Config"));
to ConfigureServices.
You can then inject the configuration in constructors by doing the following:
public class SomeController : Controller
{
private readonly IOptions<MyConfiguration> config;
public ServiceLocatorController(IOptions<MyConfiguration> config)
{
this.config = config;
}
[HttpGet]
public IActionResult Get()
{
return new HttpOkObjectResult(config.Value);
}
}
This example is for controllers. But you can do the same with other layers of you application.
I have a constructor in my repository class that accepts the db connection string as a parameter. This works for me when I add my repository for injection. In ConfigureServies() of the startup.cs file add this:
services.AddScoped<IRepos>(c => new Repos(Configuration["DbConnections:ConnStr1"]));
IRepos.cs is the interface, Repos.cs is the class that implements it. And of course Configuration is just a reference to the built IConfigurationRoot object.
A slightly different approach would be to make a static class in your Class Library on which you call a method from the Configure(..)-method in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
ConnectionManager.SetConfig(Configuration);
}
In this case, I've added Configuration as a Singleton in ConfigureServices:
services.AddSingleton(_ => Configuration);
My ConnectionManager looks like this:
public class ConnectionManager
{
private static IConfiguration currentConfig;
public static void SetConfig(IConfiguration configuration)
{
currentConfig = configuration;
}
/// <summary>
/// Get a connection to the database.
/// </summary>
public static SqlConnection GetConnection
{
get
{
string connectionString = currentConfig.GetConnectionString("MyConnection");
// Create a new connection for each query.
SqlConnection connection = new SqlConnection(connectionString);
return connection;
}
}
}
This may or may not have some issues regarding object lifetimes and such, and I'm certainly no fan of static classes but as far as I can tell it's a viable approach. Instead of passing Configuration you could even extract the ConnectionString from the config-file and send only that.
There is already an extension method you can use to get connection strings specifically from aspsettings.json.
Define your connection strings in appsettings.json like this:
{
"ConnectionStrings": {
"Local": "Data source=.\\SQLExpress;Initial Catalog=.......",
"Test:": "Data source=your-server;......"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
In your public void ConfigureServices(IServiceCollection services) method inside your Startup.cs, you can get the connection string like this:
var connectionString = this.Configuration.GetConnectionString("Local");
The GetConnectionString extension is from Microsoft.Extensions.Configuration.
Enjoy :)
UPDATE
I didn't want to go into details at first because the question here had already been marked as answered. But I guess I can show my 2 cents here.
If you do need the whole IConfigurationRoot object injected into Controllers, #Chrono showed the right way.
If you don't need the whole object, you should just get the connection string, pass it into the DbContext inside the ConfigureServices() call, and inject the DbContext into Controllers. #Prashant Lakhlani showed it correctly.
I am just saying, in #Prashant Lakhlani post, you can use GetConnectionString extension instead to clean up the code a little bit.
If ConfigureServices in your project's startUp.cs contains
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
and your repository cs file is having constructor injection as shown below
public class MyRepo : IRepo
{
private readonly YourDbContext dbContext;
public MyRepo(YourDbContext ctx)
{
dbContext = ctx;
}
}
YourDbContext will be automatically resolved.
What you need is to create a class in class library project to access the appsettings.json in website project and return connection string.
{
private static string _connectionString;
public static string GetConnectionString()
{
if (string.IsNullOrEmpty(_connectionString))
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
_connectionString = Configuration.Get<string>("Data:MyDb:ConnectionString");
}
return _connectionString;
}
public static IConfigurationRoot Configuration { get; set; }
}

Access from class library to appsetting.json or config.json in vNext

I am creating a Nuget Package and I'm working in my class library. I need access to appsetting.json or config.json to access to default connection string.
What is the best way to migrate my actual working code to the new version of ASP.NET vNext?
I have read about it in this question, but it's a fine solution for me.
Working code:
/// <summary>
/// Retrieves the default connectionstring from the App.config or Web.config file.
/// </summary>
/// <returns>Returns the default connectionstring from the App.config or Web.config file.</returns>
public static string GetDefaultConnectionString()
{
return ConfigurationManager.ConnectionStrings[DefaultConnectionstringName].ConnectionString;
}
The old class library read config values automatically with the app.config. In the new class library you have to add this functionality. The Startup.cs is use to read the app.settings In a class library you have to add a Startup.cs also.
In your project.json make sure you have the dependency
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final"
Add an appsettings.json via add - new item. Filter on json
{
"Data": {
"MyDb": {
"ConnectionString": "Server=.;Database=MyDb;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
}
You can call your connection MyDb or DefaultConnection.
Add a Startup.cs to put the code to read the appsettings.json.
See below for the Startup constructor method doing this.
e.g.
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace MyProject
{
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup()
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration["Data:MyDb:ConnectionString"]));
}
}
}
In the example above the reference
Configuration["Data:MyDb:ConnectionString]
will return a type of IConfigurationRoot, not string.
To get the string value try the following
string connection = Configuration.Get<string>("Data:MyDb:ConnectionString");

Categories

Resources