Read appsettings json values in .NET Core Test Project - c#

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.

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

appsettings.json values not binding in Azure Functions App V3

I am a new a developer in terms of dealing with creating an Azure functions App. Since we have different environments for deployment, we use environment based appsettings.json files to load the correct values from the Azure Key Vault. I followed the tutorial here: https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection for adding appsettings.json files. Here is what my Startup class looks like.
[assembly: FunctionsStartup(typeof(Trialtimer.FunctionApp.Startup))]
namespace Trialtimer.FunctionApp
{
public class Startup: FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
var config = builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"), optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
}
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<MyOptions>().Configure<IConfiguration>((options, configuration) =>
{
var section = configuration.GetSection("MyOptions");
section.Bind(options);
});
}
}
}
My appsettings.json file is empty while my appsettings.build.json file has the following, based on the tutorial above
"MyOptions": {
"MyCustomSetting": "Foobar"
}
My MyOptions class is the following
public class MyOptions
{
public string MyCustomSetting { get; set; }
}
My main class which houses the Azure Timer function is the following
public class TestTimerFunction
{
private readonly MyOptions _settings;
public TestTimerFunction(IOptions<MyOptions> options)
{
_settings = options.Value;
}
[FunctionName("AzureTimerFunction")]
public async Task Run([TimerTrigger("*/3 * * * * *")]TimerInfo myTimer, ILogger log)
{
var option = _settings;
log.LogInformation($"Renew function executed at: {DateTime.Now} successfully with {option.MyCustomSetting}");
}
}
The problem i'm having is, when i run the app, the "MyCustomSetting" variable is null. If i add the same json block that is in my appsettings.build.json file to the local.settings.json file, it looks like it gets read correctly and "MyCustomSetting" has the value "FooBar".
The ASPNETCORE_ENVIRONMENT variable is also correctly returning "build". I've checked to make sure that both the appsettings.json files are "Copy if Newer" and when hovering over the "configuration" variable in the "Configure" method, i see that my appsettings.build.json file there. How can I make the function app bind the block from appsettings.build.json rather than local.settings.json? Am i doing something incorrectly?
Got it to work by swapping out the
builder.Services.AddOptions<MyOptions>().Configure<IConfiguration>((options, configuration) =>
{
var section = configuration.GetSection("MyOptions");
section.Bind(options);
});
with
builder.Services.Configure<MyOptions>(builder.GetContext().Configuration.GetSection("MyOptions"));
I don't know why this worked while the way that was described in the tutorial did not. But this has resolved my issue so I'm marking the question as answered.

How to add an appsettings.json file to my Azure Function 3.0 configuration?

The new Azure Function 3.0 SDK provides a way to implement a Startup class. It gives access to the collection of services that are available by dependency injection, where I can add my own components and third-party services.
But I don't know how to use a configuration file.
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
...
My third party services take large structures as parameter, and those configuration files are copied with binaries. I can copy them in a subsection of an appsettings.json file:
{
"MachineLearningConfig" : {
( about 50+ parameters and subsections )
}
}
Configuration values are updated according to the environment of deployment . I use Azure Devops's File Transform Task for that: production values are different from staging and dev values.
Given the documentation https://learn.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection the way to load those options is:
builder.Services.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
But that requires to add all settings as key/value strings in the host's environment, and that is what I do not want to do. There are too many of them and that is not as easy to maintain as in a json configuration file.
I copied that appsettings.json alongside the host.json.
But the appsettings.json file read at startup by the Azure Function SDK is not my application's appsettings.json but Azure Function tools's appsettings.json. So configuration.GetSection("MachineLearningConfig") returns empty values as there is no appsettings.json file in the Azure Function tools bin folder.
So, my question: how to have my MachineLearningConfig section read from my appsetting.json file injected as IOption<MachineLearningConfig> in my app ?
In Azure Functions v3 you can use the appsettings.json configuration pattern from ASP.NET-Core with the ConfigureAppConfiguration call below (reference).
Additionally, change the way you add your options by using the code within the Configure method below. You should not be passing IConfiguration to IServiceProvider.Configure<>(). This will allow you to use an injected IOptions<MachineLearningConfig> object.
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
[assembly: FunctionsStartup(typeof(Startup))]
namespace MyAzureFunction
{
public class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
var context = builder.GetContext();
builder.ConfigurationBuilder
.AddAppsettingsFile(context)
.AddAppsettingsFile(context, useEnvironment: true)
.AddEnvironmentVariables();
}
public override void Configure(IFunctionsHostBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
var configuration = builder.GetContext().Configuration;
builder.Services.Configure<MachineLearningConfig>(options =>
{
configuration.GetSection("MachineLearningConfig").bind(options);
});
}
}
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddAppsettingsFile(
this IConfigurationBuilder configurationBuilder,
FunctionsHostBuilderContext context,
bool useEnvironment = false
)
{
if (context == null) throw new ArgumentNullException(nameof(context));
var environmentSection = string.Empty;
if (useEnvironment)
{
environmentSection = $".{context.EnvironmentName}";
}
configurationBuilder.AddJsonFile(
path: Path.Combine(context.ApplicationRootPath, $"appsettings{environmentSection}.json"),
optional: true,
reloadOnChange: false);
return configurationBuilder;
}
}
}
Nkosi's solution works pretty well, but it does update the way the azure function runtime loads settings for itself, by replacing IConfiguration singleton: services.AddSingleton<IConfiguration>.
I prefer having another IConfigurationRoot that is not injected. I just need to inject my settings IOption<MachineLearningSettings> that are linked to my own IConfigurationRoot.
I build another IConfigurationRoot that is member of the Startup class:
public class Startup : FunctionsStartup
{
private IConfigurationRoot _functionConfig = null;
private IConfigurationRoot FunctionConfig( string appDir ) =>
_functionConfig ??= new ConfigurationBuilder()
.AddJsonFile(Path.Combine(appDir, "appsettings.json"), optional: true, reloadOnChange: true)
.Build();
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<MachineLearningSettings>()
.Configure<IOptions<ExecutionContextOptions>>((mlSettings, exeContext) =>
FunctionConfig(exeContext.Value.AppDirectory).GetSection("MachineLearningSettings").Bind(mlSettings) );
}
}
Note: connection strings must remain in the application settings, because it is required by triggers to create an instance of the the function app that is not started (in a consumption service plan).
With this .NET Core 3.1 and Azure Function 3. Spent a hours days. Here is what I came up with.
[assembly: FunctionsStartup(typeof(Ugly.AzureFunctions.Startup))]
namespace Ugly.AzureFunctions
{
class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
try
{
// On Azure, we need to get where the app is.
// If you use Directory.GetCurrentDirectory(), you will get something like D:\Program Files (x86)\SiteExtensions\Functions\3.0.14785\32bit
var basePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "..");
var environmentName = builder.GetContext().EnvironmentName;
builder.ConfigurationBuilder
.SetBasePath(basePath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}
catch (Exception ex)
{
// Handle exceptions about this. Which __should__ never ever happen.
// The previous comment is sarcastic.
throw;
}
}
public override void Configure(IFunctionsHostBuilder builder)
{
try
{
// DO NOT add the configuration as Singleton.
// If you need the IConfiguration:
//var configuration = builder.GetContext().Configuration;
builder.Services
.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) => {
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
}
catch (Exception ex)
{
// Handle or not handle? That's the question.
throw;
}
}
}
}
In the startup class:
IConfigurationRoot config = new ConfigurationBuilder()
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("someSettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Add a json file to you project that holds the settings. Note that local.settings.json is ignored/removed during deployment. (Name the file something else.)
MS Docs has been updated with configuration samples
Remember to install required libraries listed in Prerequisites seciton.
using System.IO;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
}
}
After some research, I came across this thread on Githib
using appsettings.json + IConfiguration in Function App
From which I crafted the following extension based on the comments and suggestions that showed to have worked.
public static class FunctionHostBuilderExtensions {
/// <summary>
/// Set up the configuration for the builder itself. This replaces the
/// currently registered configuration with additional custom configuration.
/// This can be called multiple times and the results will be additive.
/// </summary>
public static IFunctionsHostBuilder ConfigureHostConfiguration (
this IFunctionsHostBuilder builder,
Action<IServiceProvider, IConfigurationBuilder> configureDelegate) {
IServiceCollection services = builder.Services;
var providers = new List<IConfigurationProvider>();
//Cache all current configuration provider
foreach (var descriptor in services.Where(d => d.ServiceType == typeof(IConfiguration)).ToList()) {
var existingConfiguration = descriptor.ImplementationInstance as IConfigurationRoot;
if (existingConfiguration is null) {
continue;
}
providers.AddRange(existingConfiguration.Providers);
services.Remove(descriptor);
}
//add new configuration based on original and newly added configuration
services.AddSingleton<IConfiguration>(sp => {
var configurationBuilder = new ConfigurationBuilder();
//call custom configuration
configureDelegate?.Invoke(sp, configurationBuilder);
providers.AddRange(configurationBuilder.Build().Providers);
return new ConfigurationRoot(providers);
});
return builder;
}
}
The main idea is to extract all the currently registered configuration related types, create a new builder, apply custom configuration and build a new configuration with the original and custom configuration details merged into one.
It would then be used in Startup
public class Startup : FunctionsStartup {
public override void Configure(IFunctionsHostBuilder builder) {
builder.ConfigureHostConfiguration((sp, config) => {
var executioncontextoptions = sp.GetService<IOptions<ExecutionContextOptions>>().Value;
var currentDirectory = executioncontextoptions.AppDirectory;
config
.SetBasePath(currentDirectory)
.AddJsonFile("appSettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
//if there are multiple settings files, consider extracting the list,
//enumerating it and adding them to the configuration builder.
});
builder.Services
.AddOptions<MachineLearningConfig>()
.Configure<IConfiguration>((settings, configuration) => {
configuration.GetSection("MachineLearningConfig").Bind(settings);
});
}
}
The above should now be able to get the settings from your custom configuration.
When you develop a function app locally, you must maintain local copies of these values in the local.settings.json project file. To learn more, see Local settings file.
The easiest way to upload the required settings to your function app in Azure is to use the Manage Application Settings... link that is displayed after you successfully publish your project.
Refer this example on how to get those setting values.
var config = new ConfigurationBuilder()
.SetBasePath(currentDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
here is a sample project

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)

Using IConfiguration in C# Class Library

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
}

Categories

Resources