Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to tell Application to read <runtime> from my custom app.config file instead of from default one

Let us say i am creating an app called ConsoleApp2.

Because of some third party libraries i am using, my default app.config file is generating code like

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
      <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

That is because my solution references different versions of one library, so it needs to tell everyone: "Hey, if you look for any oldVersion of this library, just use newVersion". And that is all right.

The problem is that i want to define a separate config file "test.exe.config" where i have some settings and get rid of the automatically generated one.

In order to tell my App about the new config file i am using code like

AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");

And that works (almost) perfectly. And i wrote there "almost" since although the <appSettings> section is being read correctly, the <runtime> section is not being looked at in my custom config file, but the App looks for it in the default config file instead, which is a problem since i want to be able to delete that one later.

So, how can i tell my Application to read also the <runtime> information from my custom config file?


How to reproduce the issue

A simple sample to reproduce my issue is as follows:

Create a library called ClassLibrary2 (.Net Framework v4.6) with a single class as follows

using Newtonsoft.Json.Linq;
using System;

namespace ClassLibrary2
{
    public class Class1
    {
        public Class1()
        {
            var json = new JObject();
            json.Add("Succeed?", true);

            Reash = json.ToString();
        }

        public String Reash { get; set; }
    }
}

Note the reference to Newtonsoft package. The one installed in the library is v10.0.2.

Now create a Console Application called ConsoleApp2 (.Net Framework v4.6) with a class called Program which content is simply as follows:

using System;
using System.Configuration;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {

            AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "test.exe.config");

            var AppSettings = ConfigurationManager.AppSettings;

            Console.WriteLine($"{AppSettings.Count} settings found");
            Console.WriteLine($"Calling ClassLibrary2: {Environment.NewLine}{new ClassLibrary2.Class1().Reash}");
            Console.ReadLine();

        }
    }
}

This Application should have installed also Newtonsoft, but in a different version v12.0.3.

Build the Application in Debug mode. Then, in the folder ConsoleApp2/ConsoleApp2/bin/Debug create a file called test.exe.config with following content

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <appSettings>
    <add key="A" value="1"/>
    <add key="B" value="1"/>
    <add key="C" value="1"/>
  </appSettings>
</configuration>

and note that in that same Debug folder there is also the default config file ConsoleApp2.exe.config with a content like

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

If a this point you run the application it will compile with no problems and you should see a Console like

enter image description here

Note that the (3) settings were read from my custom config file correctly. So far so good...

Now rename the default config file to something like _ConsoleApp2.exe.config and run again the application. You should now get a FileLoadException.

enter image description here

So again, how can i tell my Application to read the <runtime> information from my custom config file?


Rationale

The reason i am looking an answer to this question is as follows:

When we release our application, we put all the .exe and .dll files in one folder and our custom config file (with settings, etc) in another, where our clients have similar files.

In the folder with the .exe and .dll files we try to keep as little as possible so i was asked to find a way to get rid of that ConsoleApp2.exe.config if possible. Now, since the aforementioned bindings were written in that config file, i just tried moving that information to our custom config file... but so far i have failed to achieve: the binding redirects are always tried to be read from that ConsoleApp2.exe.config, so as soon as i remove it i get exceptions...

like image 528
deczaloth Avatar asked Dec 12 '19 20:12

deczaloth


People also ask

How read app config file in C# Windows application?

Add the App. config file to your project. After creating a . NET Framework project, right-click on your project in Solution Explorer and choose Add > New Item. Choose the Application Configuration File item and then select Add.

Can we change the configuration file at run time?

You can change and save these using the ConfigurationManager . You can modify and save user settings without restarting. You can't do that with application settings.

What happens when you modify the app config at runtime?

When you try to modify the app.config at runtime, if you don’t do it right, you’ll run into a few problems: System.Configuration.ConfigurationErrorsException: The configuration is read only.

How do I add an app config file to my project?

Visual Studio makes it easy to add an App.config file to your project. After creating a .NET Framework project, right-click on your project in Solution Explorer and choose Add > New Item. Choose the Application Configuration File item and then select Add. Open the App.config file and add the following XML within the <configuration> element.

What is app config in Visual Studio Code?

The code deals with the appSettings and connectionStrings sections. App.config is the name of the config file in Visual Studio. When the program is actually running, it’s called ExecutableName.exe.config (note: ExecutableName is a placeholder that refers to your executable file name).

How do I change the default app config in Java?

using(AppConfig.Change(tempFileName)) { // the app.config in tempFileName is used } // the default app.config is used. If you want to change the used app.config for the whole runtime of your application, simply put AppConfig.Change(tempFileName) without the using somewhere at the start of your application.


2 Answers

You're probably looking for config transforms:

The idea behind is that you create multiple configurations in Visual Studio like Debug, Release, Production, Test ... in the configuration manager and a default configuration file plus so-called transforms.

Note that you can create as many configuation as you like in the configuration manager. To add new ones, click on Solution Configurations (the dropdown showing "Debug" or "Release"), and select "Configuration Manager...". Open it, and you see a list of all currently existing configurations. Drop down the combobox "Active Solution configuration" and select "<New...>" to add more.

Those transforms specify what makes the specific configuration different from the default one - so you don't need to repeat what you have already specified in the default configuration, instead you just mention the differences, for example:

<configuration>
    <appSettings>
        <add key="ClientSessionTimeout" value="100"
            xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
    </appSettings>
</configuration>

which finds the relevant setting by its key ClientSessionTimeoutand sets its value to 100 by replacing the original value in the config file (this is what the additional transform attributes xdt:Transform="SetAttributes" xdt:Locator="Match(key)" mean). You can also specify to remove existing settings (by specifying xdt:Transform="Remove" instead), e.g.

<add key="UserIdForDebugging" xdt:Transform="Remove" xdt:Locator="Match(key)"/>

would remove a user Id that should be there only for debugging, not for the release (To find out more about available options, please look here - described for Web.config, but also applicable for App.config).

In addition to the App.Config file you have one file per configuration, i.e. App.Debug.Config for Debug, App.Release.Config for Release etc. Visual Studio helps you creating them.

I have already created answers in StackOverflow here and there, which describes it in detail, please have a look.

If you're having issues displaying them in Visual Studio, take a look here.


Regarding your Rationale:

Transforms are creating a full configuration file by applying the transform file onto the default configuration file. The resulting file is compiled and put into the "bin" folder - together with the other compiled files. So, if you have a configuration "Release" selected, then all the files including the transformed config file are compiled into "bin\Release".

And the configuration file is named just as the exe file plus ".config" appended at the end (in other words, there is no ".Release.config" in the binary folder, but a "MySuperCoolApp.exe.config" created - for the application "MySuperCoolApp.exe").

Likewise, the same is true for the other configuration - each configuration creates a subfolder inside of "bin" - if you're using scripts, that subfolder can be referenced as $(TargetDir) in a post-build event.

like image 176
Matt Avatar answered Nov 15 '22 15:11

Matt


Config transformation

Given the issue occur when you try to use another (non-native) config file you are trying to find a solution to 'properly' substitute it. In my answer I want to step back a little and focus on the reason why do you want to substitute it. Based on what you described in the question, you have it to define custom application settings. If I understood correctly, you plan to link it to the target project, set 'Copy to output' property to 'Always' and you'll get it near the application.

Instead of copying the new config file, there is a way to transform existing (native) one, in your case - ConsoleApp2.exe.config using Xdt transformations. To achieve that, you create transformation file and declare there only the sections which you want to transform, for example:

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appSettings xdt:Transform="Replace">
    <add key="A" value="1"/>
    <add key="B" value="1"/>
    <add key="C" value="1"/>
  </appSettings>
</configuration>

Benefits of such approach are:

  • flexibility: transforms are very flexible, you may replace sections, merge them, set/remove attributes, etc. You may have environment specific (DEV/UAT/PROD) or build specific(Debug/Release) transforms.
  • reusability: define transform once and reuse it in all projects you need.
  • granularity: you declare only what you need, no need to copy-paste whole config.
  • safety: you let nuget and msbuild manage 'native' config file (add binding redirects, etc)

The only disadvantage of this approach is learning curve: you need to learn syntax and know how to glue transforms to your configs in MSBuild.

.NET Core has support of transform, here is an example how to create transforms for web.config, but you can apply transforms to any configs.

If you develop .NET applications (not .NET Core) then I'd recommend to look at Slowcheetah.

There are a lot of resources and useful blogbosts about transformation, it is rather widely used. Please contact me if you'll have difficulties.

From my point of view config transforms is a right solution to achieve your goal so I'd strongly recommend to consider it instead of tweaking runtime.

Externalize config sections

If you still want to keep appSettings in common location, then you can externalize config sections with ConfigSource attribute. Check this and this thread for details:

// ConsoleApp2.exe.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings configSource="../commonConfig/connections.config"/>
</configuration>

// connections.config:
<?xml version="1.0" encoding="utf-8"?>
<connectionStrings>
<add name="MovieDBContext" 
   connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=aspnet-MvcMovie;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\Movies.mdf" 
   providerName="System.Data.SqlClient" 
/>
</connectionStrings>

AppSettings section contains File attribute that allows you to merge parameters from another file.

This option allows you to replace certain sections of the configuration, but not whole content itself. So if you need only appSettings, it is totally applicable - you just put config file with appSettings to common location shared with user and patch config file (add file or configSource attribute) to source this section from that location. If you need more sections, you'll need to extract them as separate files.

like image 28
fenixil Avatar answered Nov 15 '22 16:11

fenixil