简体   繁体   English

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

[英]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.我有一个 .Net Core 3.0 项目,并希望利用新选项将项目发布为单个 .exe 文件。

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.果然,这会产生一个单一的.exe文件,但问题是我的项目中有一个app.config文件,用户应该修改它,我不确定它是否也被打包在.exe中。 When I tested the produced.exe it reads the values I have initially put in the app.config.当我测试produced.exe 时,它会读取我最初放入app.config 的值。

I have tried setting the "Copy to Output Directory" option to "Copy always" but that didn't change anything.我尝试将“复制到 Output 目录”选项设置为“始终复制”,但这并没有改变任何内容。 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?如何设置它,以便除 app.config 之外的所有内容都打包在 that.exe 文件中,以便用户可以更改值?

Short version精简版

.NET Core doesn't use app.config , you'll have to upgrade to the new config system or manually manage the files. .NET Core 不使用app.config ,您必须升级到新的配置系统或手动管理文件。

  1. Add <ExcludeFromSingleFile>true</ExcludeFromSingleFile> to the App1.config file to keep it out of the bundle.<ExcludeFromSingleFile>true</ExcludeFromSingleFile>添加到App1.config文件以将其排除在包之外。
  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.手动添加带有生产设置的MyApp.exe.config文件并添加<CopyToPublishDirectory>Always</CopyToPublishDirectory>以将其发布到Publish目录。 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 3,即使对于 Windows Forms,也不使用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. .NET 内核的配置系统ASP.NET 内核的配置中进行了描述,尽管名称如此,但它适用于每个 .NET 内核应用程序。 It's far more powerful too, loading configuration from multiple sources, including files (even INI), databases, Azure or AWS setting stores etc.它也更强大,从多个来源加载配置,包括文件(甚至 INI)、数据库、Azure 或 AWS 设置存储等。

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.Application Configuration File添加到新的 Windows Forms 项目中,无论是在 VS 2019 中还是在命令行中,都会创建一个App1.config文件,就 VS 或 Z303CB0EF9EDB90872D61BBBE5825D9 而言没有特殊意义。 Creating an AppName.exe.config requires actually creating a new AppName.exe.config file with the production settings.创建AppName.exe.config实际上需要使用生产设置创建一个新的AppName.exe.config文件。

The only way to read an Old-style .config file is to explicitly load it with ConfigurationManager.OpenExeConfiguration .读取旧式.config文件的唯一方法是使用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.可以传递任何文件路径,或者指定一个 ConfigurationUserLevel,它只是根据可执行文件的基目录解析到文件位置。

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.使用单文件可执行文件,所有文件都捆绑在一个扩展名为.exe的主机文件中。 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.当该文件第一次运行时,它会将其内容解压缩到AppData\Local\Temp\.net\中,位于为应用程序命名的新文件夹中。 By design , the application's base directory should be the host's path, where the single.exe is. 按照设计,应用程序的基目录应该是主机的路径,即 single.exe 所在的位置。 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.不幸的是,有一个错误,基本目录仍然是包的位置和主机实际运行的.dll

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\ 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:如果我们保持命名文件appname.exe.config的旧约定,我们可以只 append .config到主机的完整路径:

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

This has to be done with the .NET Core File provider too.这也必须通过 .NET 核心文件提供程序来完成。

You should mark the file as <ExcludeFromSingleFile>true</ExcludeFromSingleFile> in the project settings.您应该在项目设置中将该文件标记为<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 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.这对我来说适用于独立的 exe。 In my.csproj file I added this which created the myProgramName.dll.config upon build.在 my.csproj 文件中,我添加了这个,它在构建时创建了 myProgramName.dll.config。

<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.在我的 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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