简体   繁体   中英

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

I have a.Net Core 3.0 project and wanted to take advantage of the new option to publish the project as a single.exe file.

I use the command
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

This, sure enough, produces a single.exe file but the problem is I have an app.config file in my project that the users should modify and I am not sure if it gets packaged in the.exe, as well. When I tested the produced.exe it reads the values I have initially put in the app.config.

I have tried setting the "Copy to Output Directory" option to "Copy always" but that didn't change anything. The "Build action" is set to "None".

How can I set it so that everything EXCEPT the app.config is packaged inside that.exe file, so that the user can change values?

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.

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

This works for me for a standalone exe. In my.csproj file I added this which created the myProgramName.dll.config upon build.

<ItemGroup>
 <None Update="App.config">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
 </None>
</ItemGroup>

And in my C# console app code (dot net core 3.1.0) I added this.

// requires using System.Configuration;
string programName = "myProgramName"; 
var sourceHostFile = Directory.GetCurrentDirectory() + @"\" + programName + @".dll.config";
Console.WriteLine("sourceHostFile is: " + sourceHostFile);
// to load yourProgram.dll.config
// 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
var targetHostFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
// ignore when in debug mode in vs ide
if (sourceHostFile != targetHostFile)
{
File.Copy(sourceHostFile, targetHostFile, true);
}
Console.WriteLine("targetHostFile is: " + targetHostFile);
string password = ConfigurationManager.AppSettings["password"];
Console.WriteLine("password from config is: " + password);
// end of poc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM