Як я можу отримати один. Файловий додаток .NET Core 3, щоб знайти файл appsettings.json?


11

Як слід налаштувати однофайловий додаток .Net Core 3.0 Web API, щоб шукати 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

Всі перераховані вище методи вказують на місце, з якого розпаковується файл, яке відрізняється від місця, з якого він був запущений.

Відповіді:


13

Я знайшов проблему на 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);
}

Ця відповідь, плюс @ ronald-swaine нижче, ідеальна. Додатки виключаються з пакетного файлу exe, а завдання публікації розміщує файли програм поряд із пакетним файлом exe.
Аарон Гудон

8

Якщо у вас все в порядку з тим, що файли, які використовуються під час виконання за межами виконуваного файлу, ви можете просто позначити потрібні файли зовні, в 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>

Яким чином Views \ Test.cshtml у вашому прикладі потрапляє на цільовий комп'ютер? У мене є файли зображень та словників, які мені потрібно відкрити на основі культури,
Пол Коен

@PaulCohen Я зазвичай розгортаю всі файли з опублікованого місця виводу за допомогою SCP. Зі змінами в csproj (перший приклад) будь-яким API-кодам, які використовують каталог за замовчуванням за замовчуванням, потрібно мати свої файли в робочому каталозі. Здається, що вам потрібно скористатися другим прикладом, щоб отримати реальний шлях вилученого контенту, щоб таким чином ви могли отримати доступ до зображень з повного шляху або правильно встановити корінь вмісту, щоб ~ / ... шляхи зображення можна використовувати в бритві.
Рональд

Моє тло у вбудованих додатках, тому один двійковий файл, як правило, спалюється в rom. Що я розумію, це Exe, яким я ділюсь у якомусь самовипаковуванні "zip like" файла. Усі мої файли даних є там, і коли програма запущена, все витягується безпосередньо до темп. Якщо я виявлю, що можу знайти свої файли даних. Це також означає, що мені потрібна певна логіка, щоб я міг налагоджувати в VS, де дані десь ще.
Пол Коен

1
У мене зараз така настройка, і вона випадковим чином перестала знаходити файли.
Від’їжджаючи

0

Моя програма на .NET Core 3.1, публікується як єдиний файл і працює як служба Windows (що може чи не може вплинути на проблему).

Запропоноване рішення з Process.GetCurrentProcess().MainModule.FileNameкорінцем вмісту працює для мене, але тільки якщо я встановити корінь вмісту в потрібному місці:

Це працює:

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseContentRoot(...);
        webBuilder.UseStartup<Startup>();
    });

Це не працює:

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .UseContentRoot(...)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.