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.
- Add
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
to theApp1.config
file to keep it out of the bundle. - Manually add a
MyApp.exe.config
file with the production settings and add<CopyToPublishDirectory>Always</CopyToPublishDirectory>
to have it published to thePublish
directory. Transformations won't run, so make sure it contains everything that's needed. - 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.