Comment une application d'API Web .Net Core 3.0 à fichier unique doit-elle être configurée pour rechercher le fichier appsettings.json
Qui se trouve dans le même répertoire que celui pour lequel l'application à fichier unique est créée?
Après avoir couru
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true
Le répertoire ressemble à ceci:
XX/XX/XXXX XX:XX PM <DIR> .
XX/XX/XXXX XX:XX PM <DIR> ..
XX/XX/XXXX XX:XX PM 134 appsettings.json
XX/XX/XXXX XX:XX PM 92,899,983 APPNAME.exe
XX/XX/XXXX XX:XX PM 541 web.config
3 File(s) 92,900,658 bytes
Cependant, la tentative d'exécution de APPNAME.exe
Entraîne l'erreur suivante
An exception occurred, System.IO.FileNotFoundException: The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\Users\USERNAME\AppData\Local\Temp\.net\APPNAME\kyl3yc02.5zs\appsettings.json'.
at Microsoft.Extensions.Configuration.FileConfigurationProvider.HandleException(ExceptionDispatchInfo info)
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
...
J'ai essayé des solutions à partir d'une question similaire, mais distincte , ainsi que d'autres questions sur le débordement de pile.
J'ai tenté de transmettre ce qui suit à SetBasePath()
Directory.GetCurrentDirectory()
environment.ContentRootPath
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
Chacun a conduit à la même erreur.
La racine du problème est que le binaire PublishSingleFile
est décompressé et exécuté à partir d'un répertoire temp
.
Dans le cas de cette application à fichier unique, l'emplacement qu'elle recherchait appsettings.json
Était le répertoire suivant:
C:\Users\USERNAME\AppData\Local\Temp\.net\APPNAME\kyl3yc02.5zs
Toutes les méthodes ci-dessus pointent vers l'endroit où le fichier est décompressé, ce qui est différent de l'endroit à partir duquel il a été exécuté.
J'ai trouvé un problème sur GitHub ici intitulé PublishSingleFile excluding appsettings not working as expected
.
Cela indiquait un autre problème ici intitulé single file publish: AppContext.BaseDirectory doesn't point to apphost directory
Dans ce document, une solution consistait à essayer Process.GetCurrentProcess().MainModule.FileName
Le code suivant a configuré l'application pour qu'elle examine le répertoire à partir duquel l'application exécutable unique a été exécutée, plutôt que l'emplacement vers lequel les fichiers binaires ont été extraits.
config.SetBasePath(GetBasePath());
config.AddJsonFile("appsettings.json", false);
L'implémentation de GetBasePath()
:
private string GetBasePath()
{
using var processModule = Process.GetCurrentProcess().MainModule;
return Path.GetDirectoryName(processModule?.FileName);
}
Si vous êtes d'accord avec l'utilisation de fichiers à l'exécution en dehors de l'exécutable, vous pouvez simplement signaler les fichiers que vous souhaitez à l'extérieur, dans csproj. Cette méthode permet des changements en direct et autres dans un emplacement connu.
<ItemGroup>
<None Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
<None Include="appsettings.Development.json;appsettings.QA.json;appsettings.Production.json;">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
<DependentUpon>appsettings.json</DependentUpon>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Views\Test.cshtml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
</ItemGroup>
Si cela n'est pas acceptable et ne doit avoir qu'un seul fichier, je passe le chemin extrait d'un seul fichier comme chemin racine dans ma configuration d'hôte. Cela permet à la configuration et au rasoir (que j'ajoute après) de retrouver ses fichiers normalement.
// when using single file exe, the hosts config loader defaults to GetCurrentDirectory
// which is where the exe is, not where the bundle (with appsettings) has been extracted.
// when running in debug (from output folder) there is effectively no difference
var realPath = Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName;
var Host = Host.CreateDefaultBuilder(args).UseContentRoot(realPath);
Remarque, pour vraiment créer un seul fichier, et pas de PDB, vous aurez également besoin de:
<DebugType>None</DebugType>