web-dev-qa-db-fra.com

Comment puis-je obtenir mon application de fichier unique .NET Core 3 pour trouver le fichier appsettings.json?

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é.

11
Jason Yandell

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);
}
12
Jason Yandell

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>
8
Ronald Swaine