使用 .Net Core 3.0 发布独立的 exe 文件并使用 app.config

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

我有一个 .Net Core 3.0 项目,想利用新选项将项目发布为单个 .exe 文件。

我用命令
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

这确实生成了一个 .exe 文件,但问题是我的项目中有一个 app.config 文件,用户应该修改它,我不确定它是否被打包在 .exe 中,还有。当我测试生成的 .exe 时,它​​会读取我最初放入 app.config.

中的值

我已经尝试将 "Copy to Output Directory" 选项设置为 "Copy always" 但这并没有改变任何东西。 "Build action" 设置为 "None"。

我如何设置它,以便将 app.config 以外的所有内容打包到该 .exe 文件中,以便用户可以更改值?

您应该在项目设置中将文件标记为 <ExcludeFromSingleFile>true</ExcludeFromSingleFile>

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

短版

.NET Core 不使用app.config,你必须升级到新的配置系统或手动管理文件。

  1. <ExcludeFromSingleFile>true</ExcludeFromSingleFile> 添加到 App1.config 文件以使其不在捆绑包中。
  2. 手动添加带有生产设置的 MyApp.exe.config 文件并添加 <CopyToPublishDirectory>Always</CopyToPublishDirectory> 以将其发布到 Publish 目录。转换不会 运行,因此请确保它包含所需的一切。
  3. 最后显式加载文件以避免应用程序基本路径解析中的错误
var hostFile=Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
ConfigurationManager.OpenExeConfiguration(hostFile+".config");

加载已发布的文件,就好像它是任何其他文件一样


.NET Core 3,即使对于 Windows 表单,也不使用 app.config。 .NET Core 的配置系统在 Configuration in ASP.NET Core 中进行了描述,尽管名称如此,但适用于每个 .NET Core 应用程序。它也更强大,从多个来源加载配置,包括文件(甚至 INI)、数据库、Azure 或 AWS 设置存储等。

在 VS 2019 和命令行中将 Application Configuration File 添加到新的 Windows Forms 项目会创建一个 App1.config 文件,就 VS 或 .NET 而言没有特殊意义核心很关心。创建 AppName.exe.config 实际上需要使用生产设置创建一个新的 AppName.exe.config 文件。

读取旧式 .config 文件的唯一方法是使用 ConfigurationManager.OpenExeConfiguration 显式加载它。这只是加载文件并解析它。可以传递任何文件路径,或指定一个 ConfigurationUserLevel,它根据可执行文件的基本目录简单地解析为文件位置。

问题就在这里。有 a bug.

对于单文件可执行文件,所有文件都捆绑在一个扩展名为 .exe 的主机文件中。当该文件 运行 第一次出现时,它将其内容解压缩到 AppData\Local\Temp\.net\,在一个以应用程序命名的新文件夹中。 By design, the application's base directory should be the host's path, where the single .exe is. Unfortunately, there's a bug 和基本目录仍然是包的位置,.dll 实际上是主机 运行。

这就是为什么

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 我敢打赌 AppContext.BaseDirectory returns C:\Users\myUser~1\AppData\Local\Temp\.net\ConsoleApp_NetCore\nk5sdmz5.ym1\

解决方法 是检索主机路径并显式加载设置文件。这意味着我们现在可以使用任何文件名。如果我们保留命名文件 appname.exe.config 的旧约定,我们可以将 .config 附加到主机的完整路径:

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

这也必须通过 .NET Core 文件提供程序来完成。

这适用于我的独立 exe。 在我的 .csproj 文件中,我添加了这个,它在构建时创建了 myProgramName.dll.config。

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

在我的 C# 控制台应用程序代码 (dot net core 3.1.0) 中,我添加了这个。

// 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