How to change location of app.config
Asked Answered
C

4

9

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

Cruller answered 3/9, 2010 at 1:18 Comment(0)
V
9

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.

Vang answered 3/9, 2010 at 5:0 Comment(2)
Hmmm. In my test of this approach (that was different than yours), GetEntryAssembly did return null. But, I didn't do ExecuteAssembly - I found the "second Main" that I had written and executed that. I'll try your approach and see if it works for me.Cruller
I think ExecuteAssembly makes the difference. At least the Docs say that GetEntryAssembly returns the executeable, or the one passed to ExecuteAssembly().Vang
H
0

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.

Hyksos answered 3/9, 2010 at 4:54 Comment(3)
The main reason for wanting to do this are that the application is installed via ClickOnce, which effectively hides the config file. However, there are settings (like TraceSource entries) that I want to be configurable on the client machine and to persist between installations (since a ClickOnce update may replace the config file).Cruller
I see - perhaps you can use AppSettings (msdn.microsoft.com/en-us/library/k4s6c3a0.aspx) for that? They support ClickOnce scenario - see msdn.microsoft.com/en-us/library/ms228995.aspx.Hyksos
I can't use AppSettings, because the .Net Framework won't look there for TraceSources (or any other framework-specific configuration).Cruller
I
0
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.

Infantilism answered 26/3, 2013 at 16:43 Comment(1)
This might be useful for some other users, but does nothing to address the OP's problem. He explicitly states why this approach can't work for him in the question. Specifically, various framework classes (WCF configuration for instance) get settings from the config file in the default location ... you can not influence them to instead use the Configuration instance in your "config" variable.Ornamented
L
0

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>
Laccolith answered 20/4, 2018 at 20:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.