How to load external App.Config dynamically in C# Console application? - c#

I have a console application and the App.Config is outside the appdomain, I want to load it programmatically(not with powershell). I already pass the path of the external App.Config as a command argument but I don't know how to load it in the appdomain once I run the application.

for load app config you can use below code:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
var readerConnectionString = builder.Build().GetSection("Parameter")
.GetSection("DbConnectionRead").Value;
and this is a JSON sample:
{
"Parameter":
{
"DbConnectionRead": ""
}
}

I am not sure whether you can edit your App.Config from outside folder. Here is something I found about reading appsettings from App.Config outside of project folder.
static void Main(string[] args)
{
IntializeConfigurationFile();
var check = ConfigurationManager.AppSettings["conn"];
}
static void IntializeConfigurationFile()
{
//ConfigurationManager.RefreshSection("appSettings");
// Get the current configuration associated
// with the application.
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Associate the auxiliary with the default
// configuration file.
System.Configuration.AppSettingsSection appSettings = config.AppSettings;
appSettings.File = "C:\\Personal\\Learning\\Docs\\App.config";
// Save the configuration file.
config.Save(ConfigurationSaveMode.Modified);
// Force a reload in memory of the
// changed section.
ConfigurationManager.RefreshSection("configuration");
}
I have place my App.Config in C:\ drive. But your App.Config should be like this, only appsetting element to be added in your App.Config.
<appSettings>
<add key="conn" value="test" />
</appSettings>
So Basically what it does is when you call the IntializeConfigurationFile() method it writes appsetting location to your local path in your filename.exe.config. In the project I have tried was added like this
Reference: AppSettingsSection Class

Related

How to acced to my app.config from c# interactive?

I initialized the interactive element with the project from the context menu of my project.
I am testing a function in C# interactive that needs to read my app.config file to get a connectionstring.
I got the next error:
No connection string named 'ccnName' could be found in the application config file.
When I use the next code, i get a null value. I suppose it is because it is not reading the app.config of my project.
ConfigurationManager.ConnectionStrings["cnnName"]
It is the only connectionstring that the default app.config has:
[data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true]
In this post from 2012 with the same topic, one engineer involved in this project said that this option was not available. I hope it is available now in 2018
So, nowadays how can i load the app.config that i want?
"Constructor" was the magic word. This may not help in your case since you've found a solution, but it might be helpful for others in the same situation.
If you inject a System.Configuration.Configuration object into the class, you don't have to rely on ConfigurationManager's static properties.
public class LibraryClass
{
private Configuration _configuration;
public LibraryClass(Configuration configuration)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public void FunctionUnderTest()
{
string connectionString = _configuration.ConnectionStrings.ConnectionStrings["cnnName"].ConnectionString;
// Connect to the database as you normally would.
}
}
In a console/GUI application and unit tests, load it like this to use {anything}.config:
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Register 'configuration' as a singleton using the container of your choice.
In a web application, load it like this to use web.config:
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~/Web.config");
// Register 'configuration' as a singleton using the container of your choice.
To use it in C# Interactive, load it using the first method and provide the dependency to the class directly:
#r "System.Configuration"
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(
new ExeconfigurationFileMap() { ExeConfigFilename = #"path\to\Arbitrary.config" },
ConfigurationUserLevel.None);
var lib = new LibraryClass(configuration);
lib.FunctionUnderTest();
Note that the section properties are an extra layer deep compared to what you would normally expect.
I think it has something to do with how ConfigurationManager's static properties work with the Configuration instance.

.Net Core ConfigureAppConfiguration adding additional sources overriding environment specific settings

When using the IConfigurationBuilder in a .NET Core 2.1 application with a Generic Host I configure 4 sources; but after the scope of ConfigureAppConfiguration there are 6 sources.
At some point 2 additional source I have already loaded are added a second time in an order that is causing appsettings.Environment.json values to be hidden. I have also tried removing the hostsettings.json configuration and verified that is not affecting this.
This is for an Azure Webjob using WebjobsSDK 3.0 and .Net Core 2.1
var builder = new HostBuilder()
.ConfigureHostConfiguration(configurationBuilder =>
{
//This is to do some basic host configuration and should only add 2 sources
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
configurationBuilder.AddJsonFile("hostsettings.json", optional: true);
configurationBuilder.AddEnvironmentVariables(prefix: "APPSETTING_ASPNETCORE_");
})
.ConfigureAppConfiguration((hostContext, configurationBuilder) =>
{
//at this point there are 0 sources in the sources
IHostingEnvironment env = hostContext.HostingEnvironment;
configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
configurationBuilder.AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appSettings.{env.EnvironmentName}.json", optional: true,
reloadOnChange: true);
configurationBuilder.AddEnvironmentVariables(prefix: "APPSETTING_ASPNETCORE_");
//at this point there are 4 sources
})
.ConfigureServices((hostContext, servicesCollection) =>
{
//now there are 6, 2 additional source that are duplicates
servicesCollection.Configure<IConfiguration>(hostContext.Configuration);
})
I expect a configuration provider with only the 4 sources, including the ChainedConfigSource, I have setup to be included. But 2 additional sources are added which are duplicates of the appsettings.json and the environment variables which I declared before loading the environment specific appsettings.environment.json.
Now when injected the into a class the appsettings.json settings were added last are returned over a appsettings.environment.json
But 2 additional sources are added which are duplicates of the appsettings.json and the environment variables
I had a similar issue with an Azure WebJob using the HostBuilder, and noticed that these 2 source were appended to the end of the list of config sources. This had undesirable results: development settings from appsettings.json overwrote the production settings fromappsettings.Production.json.
These additional sources appear to be added here by by ConfigureWebJobs.
The fix was to re-order the HostBuilder call chain so that the call to ConfigureWebJobs comes before the call to ConfigureAppConfiguration. These extra two sources are still present, but since they are now at the start of the list of configuration sources, and not at the end, they have no undesirable effects.
It is possible to reorder builder.Sources to fit your needs. For example, here is an extension method that you can use to add a new JSON file after the last one, instead of at the very end:
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddJsonFileAfterLastJsonFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
{
var jsonFileSource = new JsonConfigurationSource
{
FileProvider = null,
Path = path,
Optional = optional,
ReloadOnChange = reloadOnChange
};
jsonFileSource.ResolveFileProvider();
var lastJsonFileSource = builder.Sources.LastOrDefault(s => s is JsonConfigurationSource);
var indexOfLastJsonFileSource = builder.Sources.IndexOf(lastJsonFileSource);
builder.Sources.Insert(indexOfLastJsonFileSource == -1
? builder.Sources.Count
: indexOfLastJsonFileSource + 1, jsonFileSource);
return builder;
}
}
You can then use it like this:
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFileAfterLastJsonFile(
"appsettings.Custom.json",
optional: true,
reloadOnChange: false);
})
You can adapt this to your needs and insert it wherever you need it to be.
Reading the source code for the Hostbuilder.cs class you will see that the Configuration added in the AddHostingConfiguration gets added to the ApplicationConfiguration.
Let me show you, you can find the source at https://github.com/aspnet/AspNetCore/blob/1c3fa82908fe2cb773626b6613843213286a482b/src/Microsoft.Extensions.Hosting/HostBuilder.cs
The build call will first create the HostingConfiguration, then the AppConfiguration.
Here's the code that builds the HostingConfiguration
private void BuildHostConfiguration()
{
var configBuilder = new ConfigurationBuilder();
foreach (var buildAction in _configureHostConfigActions)
{
buildAction(configBuilder);
}
_hostConfiguration = configBuilder.Build();
}
Now in the BuildAppConfiguration you will see that the HostingConfiguration gets added ontop of the AppConfiguration
private void BuildAppConfiguration()
{
var configBuilder = new ConfigurationBuilder();
//Here _hostConfiguration gets added ontop
configBuilder.AddConfiguration(_hostConfiguration);
foreach (var buildAction in _configureAppConfigActions)
{
buildAction(_hostBuilderContext, configBuilder);
}
_appConfiguration = configBuilder.Build();
_hostBuilderContext.Configuration = _appConfiguration;
}
Now the Build functions are private and there is no way to reset/clear sources from the builder.
If you dont want to implement your own version of the HostBuilder I'd suggest not seperating the Host settings from your Appsettings
So according to the Documentation the WebHostBuilder loads the appSettings.json and appSettings.env.json files for you. But it does not say anything about the HostBuilder doing this as well, I believe this is due to a lack of documentation and I cannot determine where in the source code that is coming from.
To resolve this issue, I changed the way my configuration files are setup. Previously I had connection strings in the appSettings.json file as well as the appSettings.env.json files. So I was having an issue with the configuration files added last replacing the values from the base configuration file. I have now moved environment based settings only into the configuration files that are for each environment and only kept the settings that were global to all environments in the base configuration file.
Seems like old habits from the .NET framework configuration transformation set ups die hard. I cannot determine if the same key in a IConfiguration declared from multiple providers should be changed to the last loaded provider, I thought some documentation covered this and confirmed this but now I cannot find it.

Replacing app.config at runtime

Here is my appconfig:
<connectionStrings>
<add name ="cn" connectionString="User ID=YOUR_USER_HERE; Password=YOUR_PASS_HERE;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=.)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=.)));"/>
</connectionStrings>
I have tried this function:
public void updateConfigFile(string con)
{
//updating config file
XmlDocument XmlDoc = new XmlDocument();
//Loading the Config file
XmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
foreach (XmlElement xElement in XmlDoc.DocumentElement)
{
if (xElement.Name == "connectionStrings")
{
{
xElement.FirstChild.Attributes[1].Value = con;
}
}
}
//writing the connection string in config file
XmlDoc.Save("App.config");
}
That did not work. It ran without any error. But it did not save connectionString into appconfig file. I also tried this idea:
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["Blah"].ConnectionString = "Data Source=blah;Initial Catalog=blah;UID=blah;password=blah";
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
I get an error that says UnauthorizedAccess. ( I believe it is because of my school network)
These two options have not worked so far. I also tried to set ConfigurationManager.ConnectionStrings["cn"] to my custom connectionString. Since it is readonly, that failed too. Is there any other idea that I can replace connectionString at runtime?
Thanks in advance.
Changing the app configuration file must be done before your application starts, as certain keys are read by the runtime when your application is being bootstrapped only, and others are turned into readonly.
You can dynamically change the configuration entries of the AppSettings section only by referencing the System.Configuration assembly. All the other keys are by design read only.
If you don't want to alter the configuration before starting the application, you need to think about all the involved entities: who is going to read your connection string? If it is you, you can simply store it somewhere else or even in the AppSettings key. Instead, if external components require to read it from your configuration file, you have no chance that changing the architecture of your application in order to have a wrapper that does the changes before running your application.
The AppSettings key ca be changed in this way, after refencing the System.Configuration assembly (this is vital).
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="key1" value="configuration"/>
</appSettings>
</configuration>
Program.cs
using System;
using System.Configuration;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
System.Configuration.ConfigurationManager.AppSettings["key1"] = "changed";
var value = System.Configuration.ConfigurationManager.AppSettings["key1"];
Console.WriteLine($"This is the new key: {value}.");
}
}
}

Specify AppSettingsReader() to look in different app.config for key

I'm using the AppSettingsReader() method to get a value from a given key in the app.config file:
var value = new AppSettingsReader().GetValue("SomeKey", typeof(string)) as string;
This is done in a class which is in a seperate assembly with its own app.config file. Now if I specify the key/value pair in this app.config:
<appSettings>
<add key="SomeKey" value="MyValue" />
</appSettings>
It throws the error:
"The key 'SomeKey' does not exist in the appSettings configuration section."
Because it looks in the App.config file from my main application which is, as stated before, in a different assembly. When I put my key/value pairs in there it works properly.
Is there a way of telling AppSettingsReader() to look in the app.config of the assembly from which it is called and not in the main (parent) assembly?
For this case you can use the ConfigurationManager class. It allows you to open configuration files from various places. To open a .config file, other than the one from the .exe, you could use the method OpenMappedExeConfiguration.
string pathToOtherConfigFile = ""; //you need to specify the path
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
configMap.ExeConfigFilename = pathToOtherConfig;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
var value = config.AppSettings["SomeKey"];

Modifying appSettings "file" attribute at runtime

string appConfPath = Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)).FullName;
string fullPath = appConfPath + "\\Local\\RandFolder\\ThisOne\\application.settings.xml";
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.File = fullPath;
config.AppSettings.Settings.Add("Password", "djydyjdjtdtyjddj");
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
var temp = config.AppSettings;
I am currently trying to set the "file" attribute of my appSettings configuration and reference XML's included settings at runtime. I am unable to do this before compile because the XML settings file will change based on the local machines settings.
However, after making the above changes, the temp variable only contains the "Password" item and is unable to retrieve the other settings located in the included file path. I know the file attribute is being set but for some reason the referenced setting are still hidden. The application.settings.xml file looks like this...
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="ServerLocation" />
<add key="PerforceURI" value="yuhsgbyluvgblsg" />
</appSettings>
Any help is greatly appreciated!
I won't try to critique what you are doing, but provide you with a simple explanation of what you are seeing.
ConfigurationManager.RefreshSection refreshes the section in the Configuration instance used by the static ConfigurationManager. It does not effect the Configuration instance you created by calling OpenExeConfiguration; for that to occur you would need to call OpenExeConfiguration again.

Categories

Resources