ASP.NET 5 Config File not opening - c#

I've created a static class to use my configurations, but when I try to add the JSON file to the configuration I got an exception:
MyConfigurations.json:
{ "ConnectionString": "my connection string", ...}
My static class constructor:
static MyConfigurations() {
var configuration = new Configuration()
.AddJsonFile("MyConfigurations.json")
.AddEnvironmentVariables();
...
...
My exception occurs when the .AddJsonFile is executed.
Exception: Object reference not set to an instance of an object."
StackTrace:
at
Microsoft.Framework.ConfigurationModel.PathResolver.get_ApplicationBaseDirectory()
at
Microsoft.Framework.ConfigurationModel.JsonConfigurationExtension.AddJsonFile(IConfigurationSourceRoot
configuration, String path, Boolean optional) at
Microsoft.Framework.ConfigurationModel.JsonConfigurationExtension.AddJsonFile(IConfigurationSourceRoot
configuration, String path) at
Project.SharedKernel.MyConfigurations..cctor() in
C:\Project\Project.SharedKernel\MyConfigurations.cs:line 86

You have not set the application base path which the configuration API needs to determine where the physical config files live. You can set it using the SetBasePath extension method. A typical implementation looks like this:
public Startup(IApplicationEnvironment appEnv)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("MyConfigurations.json")
.AddEnvironmentVariables()
.Build();
}
Note: this only counts for beta8, see this question. You don't have to specify the default base path anymore in RC1: https://github.com/aspnet/Announcements/issues/88.

Related

Dotnet Core 3.1: How to use AddJsonFile with absolute path to file?

I have a dotnet app in which I need to pull in configuration from both relative paths (the regular appsettings.Json, appsettings.Development.json), and also, from the absolute path /config/appsettings.json. I can't find a way to do this. From the docs, the path arg is Path relative to the base path stored in Properties of builder. How can I set it up to read a config from absolute path /config/filename.json?
Motivation:
I'm dockerizing the dotnet app, so I am planning to keep configuration that won't usually change between deployments in the regular publish/appsettings.json and deploy specific config in /config/appsettings.json (which is a volume I'll mount on the container at deploy time).
You can edit your CreateHostBuilder method like this:
public static IHostBuilder CreateHostBuilder(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureAppConfiguration(cfgBuilder =>
{
var provider = new PhysicalFileProvider("/config"); // Create a dedicated FileProvider based on the /config directory
cfgBuilder.AddJsonFile(provider, "filename.json", true, true); // Add the filename.json configuration file stored in the /config directory
});
builder.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
return builder;
}
agnivesh Try this snippet code example:
public static class AppConfigurations
{
private static IConfigurationRoot BuildConfiguration(this IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("config/appsettings.json", optional: true, reloadOnChange: true);
//
// TODO
//
return builder.Build();
}
}
Now call this class function in Startup class contructor:
public Startup(IWebHostEnvironment env)
{
_appConfiguration = env.BuildConfiguration();
}
I use a trick.. "relative path", but the parent... you can use "../"

Can I, how do I, supply a settings file to an Azure Function?

When porting an application that uses a settings file to an Azure Function, is it necessary to remove reliance on the file?
I want to write a function app to import data from Xero into an Azure sql database.
The Xero SDK I am using is expecting an appsettings.json file.
Consequently when the function runs I get the error
System.Private.CoreLib: Exception while executing function:
FunctionXeroSync. Xero.Api: The type initializer for
'Xero.Api.Infrastructure.Applications.Private.Core' threw an exception.
Microsoft.Extensions.Configuration.FileExtensions: The configuration file
'appsettings.json' was not found and is not optional. The physical path is
'C:\Users\kirst\AppData\Local\AzureFunctionsTools\Releases\2.6.0\cli\appsettings.json'.
I tried putting the relevant settings in via the Manage Application Settings link on the VS2017 Project Publish Tab. Clearly this fails. Is there another way I can use?
Here is the relevant code in the api. I would prefer not to have to modify it, so that I can use the official nuget package.
namespace Xero.Api
{
public class XeroApiSettings : IXeroApiSettings
{
public IConfigurationSection ApiSettings { get; set; }
public XeroApiSettings(string settingspath)
{
var builder = new ConfigurationBuilder()
.AddJsonFile(settingspath)
.Build();
ApiSettings = builder.GetSection("XeroApi");
}
public XeroApiSettings() : this("appsettings.json")
{
}
public string BaseUrl => ApiSettings["BaseUrl"];
public string CallbackUrl => ApiSettings["CallbackUrl"];
public string ConsumerKey => ApiSettings["ConsumerKey"];
public string ConsumerSecret => ApiSettings["ConsumerSecret"];
public string SigningCertificatePath => ApiSettings["SigningCertPath"];
public string SigningCertificatePassword => ApiSettings["SigningCertPassword"];
public string AppType => ApiSettings["AppType"];
public bool IsPartnerApp => AppType?.Equals("partner", StringComparison.OrdinalIgnoreCase) ?? false;
}
}
When I add
log.LogInformation("base directory: "+AppDomain.CurrentDomain.BaseDirectory);
to the function I get
D:\Program Files (x86)\SiteExtensions\Functions\2.0.12095-alpha\32bit\
when running in the portal
When porting an application that uses a settings file to an Azure Function, is it necessary to remove reliance on the file?
Not necessary, we can still use the settings file required by the application. We only need to make sure the path of settings file is correct.
Put appsettings.json under function project and set it to be copied to output/publish directory.
Add ExecutionContext context in Azure Function method signature, it's used to find current function app directory(where appsettings.json locates).
Pass the valid path of appsettings.json in Azure Function to initialize XeroApiSettings.
var xeroApiSettings = new XeroApiSettings(context.FunctionAppDirectory+"/appsettings.json");
This Jon Gallant's blog suggests that you need to add the optional parameter to the AddJsonFile as it does not exist when you deploy:
var config = new ConfigurationBuilder()
.SetBasePath(context.FunctionAppDirectory)
.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
Note that in Azure, this will refer to the 'appsettings.json' file

AppSettings.json for Integration Test in ASP.NET Core

I am following this guide. I have a Startup in the API project that uses an appsettings.json configuration file.
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
}
The particular part I'm looking at is the env.ContentRootPath. I did some digging around and it looks like my appsettings.json isn't actually copied to the bin folder but that is fine as ContentRootPath is returning MySolution\src\MyProject.Api\, which is where the appsettings.json file is located.
So in my integration test project I have this test:
public class TestShould
{
private readonly TestServer _server;
private readonly HttpClient _client;
public TestShould()
{
_server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task ReturnSuccessful()
{
var response = await _client.GetAsync("/monitoring/test");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Equal("Successful", responseString);
}
This is basically copy and paste from the guide. When I debug this test, ContentRootPath is actually MySolution\src\MyProject.IntegrationTests\bin\Debug\net461\, which is obviously the build output folder for the test project and again the appsettings.json file is not there (yes I do have another appsettings.json file in the test project itself) so the test fails at creating the TestServer.
I tried getting around this by modifying the test project.json file.
"buildOptions": {
"emitEntryPoint": true,
"copyToOutput": {
"includeFiles": [
"appsettings.json"
]
}
}
I hoped this would copy the appsettings.json file to the build output directory but it complains about the project missing a Main method for an entry point, treating the test project like a console project.
What can I do to get around this? Am I doing something wrong?
Integration test on ASP.NET.Core 2.0 follow MS guide ,
You should right click appsettings.json set its property Copy to Output directory to Copy always
And now you could find the json file in output folder, and build TestServer with
var projectDir = GetProjectPath("", typeof(TStartup).GetTypeInfo().Assembly);
_server = new TestServer(new WebHostBuilder()
.UseEnvironment("Development")
.UseContentRoot(projectDir)
.UseConfiguration(new ConfigurationBuilder()
.SetBasePath(projectDir)
.AddJsonFile("appsettings.json")
.Build()
)
.UseStartup<TestStartup>());
/// Ref: https://stackoverflow.com/a/52136848/3634867
/// <summary>
/// Gets the full path to the target project that we wish to test
/// </summary>
/// <param name="projectRelativePath">
/// The parent directory of the target project.
/// e.g. src, samples, test, or test/Websites
/// </param>
/// <param name="startupAssembly">The target project's assembly.</param>
/// <returns>The full path to the target project.</returns>
private static string GetProjectPath(string projectRelativePath, Assembly startupAssembly)
{
// Get name of the target project which we want to test
var projectName = startupAssembly.GetName().Name;
// Get currently executing test project path
var applicationBasePath = System.AppContext.BaseDirectory;
// Find the path to the target project
var directoryInfo = new DirectoryInfo(applicationBasePath);
do
{
directoryInfo = directoryInfo.Parent;
var projectDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, projectRelativePath));
if (projectDirectoryInfo.Exists)
{
var projectFileInfo = new FileInfo(Path.Combine(projectDirectoryInfo.FullName, projectName, $"{projectName}.csproj"));
if (projectFileInfo.Exists)
{
return Path.Combine(projectDirectoryInfo.FullName, projectName);
}
}
}
while (directoryInfo.Parent != null);
throw new Exception($"Project root could not be located using the application root {applicationBasePath}.");
}
Ref: TestServer w/ WebHostBuilder doesn't read appsettings.json on ASP.NET Core 2.0, but it worked on 1.1
In the end, I followed this guide, specifically the Integration Testing section towards the bottom of the page. This removes the need to copy the appsettings.json file to the output directory. Instead it tells the test project the actual directory of the web application.
As for copying the appsettings.json to the output directory, I also managed to get that to work. Combined with the answer from dudu, I used include instead of includeFiles so the resulting section would look something like this:
"buildOptions": {
"copyToOutput": {
"include": "appsettings.json"
}
}
I'm not entirely sure why this works but it does. I had a quick look at the documentation but couldn't find any real differences and since the original problem was essentially solved I didn't look further.
Remove "emitEntryPoint": true in test project.json file.

.NET console app not reading config.json

While building a .net console app which uses the new ConfigurationBuilder implementation for what used to be appSettings, I've run into a problem.
I have the following code:
public static void Main(string[] args)
{
try
{
var builder = new ConfigurationBuilder().AddJsonFile("config.json");
var config = builder.Build();
if (config["Azure"] != null)
{
;
}
}
catch (System.IO.FileNotFoundException exception)
{
...
}
}
The config.json file is in the same directory and looks like this:
{
"Azure": {
"Storage": {
"ConnectionString": "...",
"ContainerName": "..."
}
},
"Data": {
"DefaultConnection": {
"ConnectionString": "..."
}
},
"Logging": {
"RecordProgress": "..."
}
}
But the config object contains no keys.
I read somewhere that if the file path passed to AddJsonFile can't be found then it throws a FileNotFoundException but in my code that exception is never thrown.
So presuming the config.json file can be found, why are the settings not being loaded?
My original answer was off the mark. Here's an updated version. This is based on the recently released RC2.
The current runtime will throw FileNotFoundException if the config file isn't found. The AddJsonFile() extension method takes an optional parameter called optional that, if true, will cause the method not to throw.
I added config.json and it doesn't get copied to the bin directory, so I had to specify the location using the SetBasePath() extension method. This is what the Web project templates do in Startup, using IHostingEnvironment.ContentRootPath. In console apps, you can use Directory.GetCurrentDirectory().
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config.json");
var config = builder.Build();
Finally, the config["Key"] indexer didn't work for me. Instead, I had to use the GetSection() extension method. So your example config file above might be accessed with:
// var result = config["Logging"];
var section = config.GetSection("Logging");
var result = section["RecordProgress"];
I left the old answer for the time being.
Old answer:
I found a possible solution here: https://github.com/aspnet/Mvc/issues/4481.
Quoting from the issue.
Thanks for the repro project. Looks like you need to update the
project.json file to have the "content" node and have Config.json
specified here.
Example:
https://github.com/aspnet/MusicStore/blob/dev/src/MusicStore/project.json#L22
It appears that a new content element may be required in your project.json.
...
"content": [
"Areas",
"Views",
"wwwroot",
"config.json",
"web.config"
],
...
I encountered the same problem.
config.json was in the correct directory and it was still not found.
The solution:
config.json contained a typo that broke the json syntax. After fixing the json syntax/format in config.json everything worked.

ExeConfigurationFileMap: InvalidArgumentException, The string parameter 'fileMap.ExeConfigFilename' cannot be null or empty

I am getting an InvalidArgumentException when I run my application. I am attempting to create a new ExeConfigurationFileMap, and then load it with ConfigurationManager.
public static ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config");
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(Settings.configFile, ConfigurationUserLevel.None);
I have also attempted to set the configuration file after declaring it, but still didn't have any luck.
If I attempt to continue after this exception, I next receive a TypeInitalizationException upon trying to set a variable to one of the values in the configuration file.
Ran into this same problem. For some ridiculous reason, initializing ExeConfigurationFileMap even with the filepath does not set the property ExeConfigFilename which is required by the Configuration objects constructor. I fixed it by immediately setting that property after instantiating the ExeConfigurationFileMap object like below:
public static ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config");
configFile.ExeConfigFilename = "QuikSnap.config";
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(Settings.configFile, ConfigurationUserLevel.None);
You didn't set the right property with the value of the config file path.
Also, you have a static variable dependency on another static variable in the same class. There could be potentially an issue of order of execution here (though i'm not sure)
Try this instead:
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
{
ExeConfigFilename = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config"
}, ConfigurationUserLevel.None);

Categories

Resources