Как получить однофайловое приложение .NET Core 3 для поиска файла appsettings.json? - PullRequest
3 голосов
/ 09 октября 2019

Как настроить однофайловое приложение веб-API .Net Core 3.0 для поиска файла appsettings.json, который находится в том же каталоге, в котором создается однофайловое приложение?

После запуска

dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true

Каталог выглядит следующим образом:

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

Однако попытка запустить APPNAME.exe приводит к следующей ошибке

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()
...

Я пробовал решения из аналогичный, но отдельный вопрос , а также другие вопросы о переполнении стека.

Я попытался передать следующее SetBasePath()

  • Directory.GetCurrentDirectory()

  • environment.ContentRootPath

  • Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)

Каждый из них приводил к одной и той же ошибке.

Корень проблемы в том, что двоичный файл PublishSingleFile разархивировани запустить из каталога temp.

В случае этого приложения с одним файлом местоположение, которое оно искало appsettings.json, было следующим каталогом:

C:\Users\USERNAME\AppData\Local\Temp\.net\APPNAME\kyl3yc02.5zs

Все вышеперечисленные методы указывают на место, где находится файлразархивирован, отличается от того места, откуда он был запущен.

Ответы [ 2 ]

3 голосов
/ 28 октября 2019

Если вы согласны с тем, чтобы файлы использовались во время выполнения вне исполняемого файла, тогда вы можете просто пометить нужные файлы снаружи, в csproj. Этот метод позволяет вносить изменения в реальном времени и т. Д. В известном месте.

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

Если это недопустимо и должно иметь ТОЛЬКО один файл, я передаю путь, извлеченный из одного файла, как корневой путь вмой хост настройки. Это позволяет конфигурации и бритве (которую я добавлю после) найти свои файлы как обычно.

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

Обратите внимание, что для создания одного файла без PDB вам также потребуется:

<DebugType>None</DebugType>
2 голосов
/ 09 октября 2019

Я нашел проблему на GitHub здесь с названием PublishSingleFile excluding appsettings not working as expected.

Это указывало на другую проблему здесь с названием single file publish: AppContext.BaseDirectory doesn't point to apphost directory

ВТаким образом, решение состояло в том, чтобы попытаться Process.GetCurrentProcess().MainModule.FileName

Следующий код настраивал приложение для просмотра каталога, из которого запускалось одноисполняемое приложение, а не места, куда были извлечены двоичные файлы.

config.SetBasePath(GetBasePath());
config.AddJsonFile("appsettings.json", false);

Реализация GetBasePath():

private string GetBasePath()
{
    using var processModule = Process.GetCurrentProcess().MainModule;
    return Path.GetDirectoryName(processModule?.FileName);
}
...