Publishing a standalone exe file with .Net Core 3.0 and using an app.config

You should mark the file as <ExcludeFromSingleFile>true</ExcludeFromSingleFile> in the project settings.

https://github.com/dotnet/core-setup/issues/7738 https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md#build-system-interface


Short version

.NET Core doesn't use app.config, you'll have to upgrade to the new config system or manually manage the files.

  1. Add <ExcludeFromSingleFile>true</ExcludeFromSingleFile> to the App1.config file to keep it out of the bundle.
  2. Manually add a MyApp.exe.config file with the production settings and add <CopyToPublishDirectory>Always</CopyToPublishDirectory> to have it published to the Publish directory. Transformations won't run, so make sure it contains everything that's needed.
  3. Finally load the file explicitly to avoid a bug in the application's base path resolution
var hostFile=Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
ConfigurationManager.OpenExeConfiguration(hostFile+".config");

To load the published file, as if it were any other file


.NET Core 3, even for Windows Forms, doesn't use app.config. .NET Core's configuration system is described in Configuration in ASP.NET Core and despite the name, applies to every .NET Core application. It's far more powerful too, loading configuration from multiple sources, including files (even INI), databases, Azure or AWS setting stores etc.

Adding an Application Configuration File to a new Windows Forms project, both in VS 2019 and the command line creates an App1.config file with no special meaning as far as VS or .NET Core are concerned. Creating an AppName.exe.config requires actually creating a new AppName.exe.config file with the production settings.

The only way to read an Old-style .config file is to explicitly load it with ConfigurationManager.OpenExeConfiguration. This simply loads the file and parses it. One could pass any file path, or specify a ConfigurationUserLevel which simply resolves to a file location based on the executable's base directory.

And here's where the trouble starts. There's a bug.

With Single-file executables, all files are bundled in a single host file with the .exe extension. When that file runs for the first time, it unpacks its contents to AppData\Local\Temp\.net\, in a new folder named for the application. By design, the application's base directory should be the host's path, where the single .exe is. Unfortunately, there's a bug and the base directory remains the location of the bundle and the .dll that's actually run by the host.

That's why

System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None).FilePath 

returns C:\Users\myUser~1\AppData\Local\Temp\.net\ConsoleApp_NetCore\nk5sdmz5.ym1\ConsoleApp_NetCore.dll.config and I'd bet AppContext.BaseDirectory returns C:\Users\myUser~1\AppData\Local\Temp\.net\ConsoleApp_NetCore\nk5sdmz5.ym1\

The workaround for this is to retrieve the host's path and load the settings file explicitly. This means we can now use any file name. If we keep the old convention of naming the file appname.exe.config, we can just append .config to the host's full path:

var hostFile=Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
ConfigurationManager.OpenExeConfiguration(hostFile+".config");

This has to be done with the .NET Core File provider too.