I want to change the location where my application looks for the app.config file.
I know that I can use ConfigurationManager.OpenExeConfiguration() to access an arbitrary config file - however, when the .Net Framework reads the config file (for ConnectionStrings or EventSources, for instance), it will look at the default location. I want to actually change the location, globally for the entire .Net Framework (for my application, of course).
I also know that I can use AppDomainSetup to change the location of the app.config for a new AppDomain. However, that doesn't apply to the primary AppDomain of the application.
I also know that I can override function Main() and create a new AppDomain as above and run my application in that new AppDomain. However, that has other side-effects - for instance, Assembly.GetEntryAssembly() will return a null reference.
Given how everything else works in .Net, I would expect there to be some way to configure the startup environment of my application - via a Application Manifest, or some such - but I have been unable to find even a glimmer of hope in that direction.
Any pointer would be helpful.
David Mullin
I used the approach with starting another AppDomain from Main(), specifying the "new" location of the configuration file.
No issues with GetEntryAssembly(); it only returns null, when being called from unmanaged code - or at least it doesn't for me, as I use ExecuteAssembly() to create/run the second AppDomain, much like this:
int Main(string[] args)
{
string currentExecutable = Assembly.GetExecutingAssembly().Location;
bool inChild = false;
List<string> xargs = new List<string>();
foreach (string arg in xargs)
{
if (arg.Equals("-child"))
{
inChild = true;
}
/* Parse other command line arguments */
else
{
xargs.Add(arg);
}
}
if (!inChild)
{
AppDomainSetup info = new AppDomainSetup();
info.ConfigurationFile = /* Path to desired App.Config File */;
Evidence evidence = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain(friendlyName, evidence, info);
xargs.Add("-child"); // Prevent recursion
return domain.ExecuteAssembly(currentExecutable, evidence, xargs.ToArray());
}
// Execute actual Main-Code, we are in the child domain with the custom app.config
return 0;
}
Note that we are effectively rerunning the EXE, just as a AppDomain and with a different config. Also note that you need to have some "magic" option that prevents this from going on endlessly.
I crafted this out from a bigger (real) chunk of code, so it might not work as is, but should illustrate the concept.
I am not sure why you want to change the location of your config file - perhaps there can be different approach for solving your actual problem. I had a requirement where I wanted to share configuration file across related applications - I had chosen to use own xml file as it had given me extra benefit of having complete control over the schema.
In your case, it's possible to externalize sections of your config file to a separate file using configSource property. See here under "Using External Configuration Files" to check how it has been done for connection strings section. Perhaps, this may help you.
var configPath = YOUR_PATH;
if (!Directory.Exists(ProductFolder))
{
Directory.CreateDirectory(ProductFolder);
}
if (!File.Exists(configPath))
{
File.WriteAllText(configPath, Resources.App);
}
var map = new ExeConfigurationFileMap
{
ExeConfigFilename = configPath,
LocalUserConfigFilename = configPath,
RoamingUserConfigFilename = configPath
};
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
Then use config member as you want.
Another approach is to leave the config file with the executable file and move the relevant changeable sections to external xml files which can be in whatever location you choose.
If you are using your config file in a readonly capacity, then you can add the relevant chunks to an XML file in a different location using XML Inlcude. This won't work if you are trying to write values back directly to app.config using the Configuration.Save method.
app.config:
<?xml version="1.0"?>
<configuration xmlns:xi="http://www.w3.org/2001/XInclude">
<appSettings>
<xi:include href="AppSettings.xml"/>
</appSettings>
<connectionStrings>
<xi:include href="ConnectionStrings.xml"/>
</connectionStrings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/></startup>
</configuration>
ConnectionStrings.xml:
<?xml version="1.0"?>
<add name="Example1ConnectionString"
connectionString="Data Source=(local)\SQLExpress;Initial Catalog=Example1DB;Persist Security Info=True;User ID=sa;Password=password"
providerName="System.Data.SqlClient" />
<add name="Example2ConnectionString"
connectionString="Data Source=(local)\SQLExpress;Initial Catalog=Example2DB;Persist Security Info=True;User ID=sa;Password=password"
providerName="System.Data.SqlClient" />
AppSettings.xml:
<?xml version="1.0"?>
<add key="Setting1" value="Value1"/>
<add key="Setting2" value="Value2"/>
A file URI looks like this:
file:///C:/whatever.txt
You can even define failover files in case the one you are trying to reference is missing. This pattern is from https://www.xml.com/pub/a/2002/07/31/xinclude.html:
<xi:include href="http://www.whitehouse.gov/malapropisms.xml">
<xi:fallback>
<para>
This administration is doing everything we can to end the stalemate in
an efficient way. We're making the right decisions to bring the solution
to an end.
</para>
</xi:fallback>
Related
This is my current solution structure
Solution ABC
ProjectA
ProjectB
ProjectWeb
AppData --> CMS --> Configurations -->oneABC.config
Areas -->Produts --> Web --> abc.cs [where I need to access that config]
Web.config
ProjectC
PFB oneABC.config file
<?xml version="1.0" encoding="utf-8"?>
<oneABC xmlns:config="urn:configuration" xmlns:type="urn:configuration:type" config:version="12.2.7239.0">
<countrycredentials>
<add name="abbpoland" password="d0da0956b411" view="True" code="PL" />
<add name="abbitaly" password="25430aa9fa22" view="True" code="IT" />
</countrycredentials>
</oneAbbExternalDistributorInventoryConfig>
its not part of web.config and the value inside oneABC.config are not static as we can have multiple add or single add as well.
Right now i need to fetch the details from this config file and use it to do some operations, I have done some digging to fetch these details but found none related to my problem.
PFB the function that i have written inside abc.cs to fetch the details:
//in culture im getting lang like 'en' ,'pl' or 'it' as per the url
public static bool GetConfigurationValue(string culture)
{
string lang = culture;// im storing that language
ExeConfigurationFileMap customConfigFileMap = new ExeConfigurationFileMap();
// in below i need to mention the path of config file right now its not working as its checking this file from C:\Program Files (x86)\IIS Express\OneABC.Config
customConfigFileMap.ExeConfigFilename = "OneABC.Config";
Configuration customConfig = ConfigurationManager.OpenMappedExeConfiguration(customConfigFileMap, ConfigurationUserLevel.None);
AppSettingsSection appSettings = (customConfig.GetSection("countrycredentials") as AppSettingsSection);
// below i need to save those tag value in such a way that if lang = PL then ill use view as true and return true
//and if ill get it in lang then ill use view as false and return false
}
is there any universal way to set the file path of this file since currently im checking this in local and the ill need to deploy it to some server so file path will change.
When I try to access configuration section that is encrypted and cannot be decrypted properly (for example, someone just grabbed config file from another machine blindly) - Configuration class is throwing an exception. I want to catch that situation and rewrite the section completely in such case.
I've tried to remove and add back the section in question, but it seems that removal is ignored - second statement in 'catch' throws another exception about such section already exists:
try
{
// this getter might throw an exception - e.g. if encrypted on another machine
var connStrings = config.ConnectionStrings;
}
catch (ConfigurationException)
{
config.Sections.Remove("connectionStrings");
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
}
It might be related to the fact I have connectionStrings section residing in separate file, i.e. my config file has something like <connectionStrings configSource="connections.config"/>, while actual encrypted content is in the connections.config file.
Is it possible to do what I need without falling back to direct XML manipulations, by using .NET Configuration classes only?
I'm pretty sure this will do what you want. In my case, I just included a bogus connectionString setting:
<connectionStrings>
<add connectionString="foo"/>
</connectionStrings>
I didn't include a "name" property, so trying to read ConnectionStrings will blow up, just like in your case:
try {
var x = ConfigurationManager.ConnectionStrings; //blows up
}
catch {
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.Sections.Remove("connectionStrings");
config.Save(); //Now save the changes
ConfigurationManager.RefreshSection("connectionStrings"); //and reload the section
var x = ConfigurationManager.ConnectionStrings; //can now read!
}
You still won't be able to do this:
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
Because (at least on my machine), it's picking up connectionStrings from the Machine.config.
I suspect though that in your case that's fine. You are now at a point where if you want to, you can add your own connection strings and you don't really need to completely blow away the connection string section.
I'm trying to read some configuration in my global.aspx Application_Start method. When I read ConfigurationManager.GetSection("system.web/httpHandlers") everything is fine:
ConfigurationManager.GetSection("system.web/httpHandlers")
{System.Web.Configuration.HttpHandlersSection}
base {System.Configuration.ConfigurationSection}: {System.Web.Configuration.HttpHandlersSection}
Handlers: Count = 48
But when I read ConfigurationManager.GetSection("system.webServer/handlers") (which contains my custom handlers, it returns null. What am I doing wrong?
The section looks like this:
<system.webServer>
<handlers>
<add verb="*" path="*.loc" name="LocalizedResourceStreamer"
type="CFW.WebUI.HttpHandlers.LocalizedResourceStreamer,WebUI" />
</handlers>
</system.webServer>
Notes:
Web.configs are nested, ConfigurationManager.GetSection takes nesting into account by default.
The overall problem is trying to see if *.loc files are being served.
So far:
Looks like the system.webServer is ignored.
Depending on your OS/setup, the system.webServer element may be configured to be ignored - and so the config system will not be constructing any inner configuration items from it. E.g. on my machine (WinXP, IIS 5.1), it's set to ignored by default.
Check the machine.config on the machine where this code is running, and see how the system.webServer element is configured. I don't have machines available with suitable later OSes at the moment, but it may be that this element is always set to be ignored - after all, that part of the config is for IIS' use, rather than our own.
try :
**p.s. my web.config contains : <httpHandlers> and not handlers as yours. change as necessarily :) - also the webserver vs system.web **
Configuration webConfig = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
ConfigurationSection webConfigSections = webConfig.GetSection("system.web/httpHandlers");
if (webConfigSections != null)
{
// PropertyInformationCollection t = webConfigSections.ElementInformation.Properties;
XDocument xmlFile = XDocument.Load(new StringReader(webConfigSections.SectionInformation.GetRawXml()));
IEnumerable<XElement> query = from c in xmlFile.Descendants("add") select c;
foreach (XElement band in query)
{
}
}
p.s. thats the problem with this section - he doesnt have a uniquee element name that can be taken. thats why you take it whole("add" element) and parse it.
I am trying to use a config file in my C# console application. I created the file within the project by going New --> Application Configuration File, and naming it myProjectName.config. My config file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="SSDirectory" value="D:\Documents and Settings\****\MyDocuments\****" />
</appSettings>
</configuration>
The code to access it looks like this:
private FileValidateUtil()
{
sSDirFilePath = ConfigurationSettings.AppSettings["SSDirectory"];
if (sSDirFilePath == null)
Console.WriteLine("config file not reading in.");
}
Can anyone lend a hint as to why this is not working? (I am getting the error message.)
Thanks!!
badPanda
You can't change the name from app.config and expect ConfigurationManager to find it without providing it more information. Change the name of myProjectName.config back to app.config, rebuild, and you will see a file in the bin folder called myProjectName.exe.config. Then your call to ConfigurationManager.AppSettings should work correctly.
check the documentation
http://msdn.microsoft.com/en-us/library/aa730869(VS.80).aspx
First off, use ConfigurationManager instead of ConfigurationSettings.
Second, instead of saying "doesn't work", which provides no useful information, tell us what you're seeing. Does it compile? Does it throw an exception at runtime? Does your PC start to smoke and smell like melting plastic?
Try this:
public string GetSSDirectory()
{
string sSDirFilePath = string.Empty;
if (!ConfigurationManager.AppSettings.AllKeys.Contains("SSDirectory"))
{
Console.WriteLine("AppSettings does not contain key \"SSDirectory\"");
}
else
{
sSDirFilePath = ConfigurationManager.AppSettings["SSDirectory"];
Console.WriteLine("AppSettings.SSDirectory = \"" + sSDirFilePath + "\"");
}
return sSDirFilePath;
}
Has anyone got this working in a web application?
No matter what I do it seems that my appSettings section (redirected from web.config using appSettings file=".\Site\site.config") does not get reloaded.
Am I doomed to the case of having to just restart the application? I was hoping this method would lead me to a more performant solution.
Update:
By 'reloading' I mean refreshing ConfigurationManager.AppSettings without having to completely restart my ASP.NET application and having to incur the usual startup latency.
Make sure you are passing the correct case sensitive value to RefreshSection, i.e.
ConfigurationManager.RefreshSection("appSettings");
This seems to be a flaw (maybe a bug) when using an external config file for your appSettings. I've tried it using the configSource attribute and RefreshSection simply never works, I'm assuming this is the same when using the file attribute.
If you move your appSettings back inside your web.config RefreshSection will work perfectly but otherwise I'm afraid you're doomed.
For some reason ConfigurationManager.RefreshSection("appSettings") wasn't working for me. Reloading the Web.Config into a Configuration object seems to work correctly. The following code assumes the Web.Config file is one directory below the executing (bin) folder.
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap();
Uri uriAssemblyFolder = new Uri(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase));
string appPath = uriAssemblyFolder.LocalPath;
configMap.ExeConfigFilename = appPath + #"\..\" + "Web.config";
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);
And is used like:
string webConfigVariable = config.AppSettings.Settings["webConfigVariable"].Value;
.RefreshSection() does not work when the appSettings is external.
You can however use the following to change a value:
ConfigurationManager.AppSettings.Set(key, value)
This will NOT change the setting on file, only the loaded value in memory.
So instead of using RefreshSection I did the following:
string configFile="path to your config file";
XmlDocument xml = new XmlDocument();
xml.Load(configFile);
foreach (XmlNode node in xml.SelectNodes("/appSettings/add"))
{
string key = node.Attributes["key"].Value;
string value= node.Attributes["value"].Value;
ConfigurationManager.AppSettings.Set(key, value);
}
Any subsequent calls to AppSettings.Get will contain the updated value.
The appSettings will then be updated without needing to restart the application.
As an alternative you could write your own ConfigSection and set restartOnExternalChanges="false".
Then, when reading the section with ConfigurationManager.GetSection("yourSection") the settings will be auto-refreshed without an application restart.
And you could implement your settings strongly typed or as NameValueCollection.
Yes. you are stuck with iis restarting.
There is a feature with asp.net 4.0 and iis 7.5 where the initial startup is removed.
I am not sure if this is possible in a web app, but it works in a desktop app. Try using ConfigurationSettings rather than ConfigurationManager (it will yell at you for using outdated classes...), then reading all the data into a class. When you wish to refresh, simply create a new instance and drop all references to the old instance. My theory for why this works (might be wrong): when you don't directly access the app.config file the entire time you are running, the file lock is dropped by the application. Then, edits can be made when you are not accessing the file.
The App.Config settings are cached in memory when the application starts. For this reason, I don't think you'll be able to change those settings without restarting your application. One alternative that should be pretty straight forward would be to create a separate, simple XML configuration file, and handle loading/caching/reloading it yourself.
To write, call it this way:
Dim config As System.Configuration.Configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~")
Return AddOrUpdateAppSetting(config, "YourSettingKey", "YourValueForTheKey")
To read and be sure you get the values in file, instead of those in cache, read it this way:
Dim config As System.Configuration.Configuration = WebConfigurationManager.OpenWebConfiguration("~")
Return config.AppSettings.Settings("TheKeyYouWantTheValue").Value
Full example:
Protected Shared Function AddOrUpdateAppSetting( _
ByVal Config As System.Configuration.Configuration _
, ByVal TheKey As String _
, ByVal TheValue As String _
) As Boolean</p>
Dim retval As Boolean = True
Dim Itm As System.Configuration.KeyValueConfigurationElement = _
Config.AppSettings.Settings.Item(TheKey)
If Itm Is Nothing Then
If Config.AppSettings.Settings.IsReadOnly Then
retval = False
Else
Config.AppSettings.Settings.Add(TheKey, TheValue)
End If
Else
' config.AppSettings.Settings(thekey).Value = thevalue
If Itm.IsReadOnly Then
retval = False
Else
Itm.Value = TheValue
End If
End If
If retval Then
Try
Config.Save(ConfigurationSaveMode.Modified)
Catch ex As Exception
retval = False
End Try
End If
Return retval
End Function
Have you tried storing your AppSettings in its own external file?
From app.config/web.config:
<appSettings configSource="appSettings.config"></appSettings>
appSettings.config:
<?xml version="1.0"?>
<appSettings>
<add key="SomeKey" value="SomeValue" />
</appSettings>
Changes made to appSettings.config should be reflected instantly.
More info:
http://msdn.microsoft.com/en-us/library/system.configuration.sectioninformation.configsource.aspx