Jak mogę uzyskać aplikację z jednym plikiem .NET Core 3, aby znaleźć plik appsettings.json?

11

Jak należy skonfigurować aplikację interfejsu API sieci Web z jednym plikiem .Net Core 3.0 w celu wyszukiwania appsettings.jsonpliku znajdującego się w tym samym katalogu, w którym zbudowana jest aplikacja z jednym plikiem?

Po bieganiu

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

Katalog wygląda następująco:

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

Jednak próba uruchomienia APPNAME.exepowoduje następujący błąd

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

Próbowałem rozwiązań z podobnego, ale odrębnego pytania , a także innych pytań dotyczących przepełnienia stosu.

Próbowałem przekazać następujące informacje SetBasePath()

  • Directory.GetCurrentDirectory()

  • environment.ContentRootPath

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

Każdy z nich doprowadził do tego samego błędu.

Głównym problemem jest to, że PublishSingleFileplik binarny jest rozpakowywany i uruchamiany z tempkatalogu.

W przypadku tej aplikacji z pojedynczym plikiem lokalizacją, której szukała, appsettings.jsonbył następujący katalog:

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

Wszystkie powyższe metody wskazują miejsce, do którego rozpakowany jest plik, który jest inny niż miejsce, z którego został uruchomiony.

Jason Yandell
źródło

Odpowiedzi:

13

Znalazłem tutaj problem na GitHub zatytułowany PublishSingleFile excluding appsettings not working as expected.

Wskazuje to na inną kwestię zatytułowaną tutajsingle file publish: AppContext.BaseDirectory doesn't point to apphost directory

W tym rozwiązaniem było spróbować Process.GetCurrentProcess().MainModule.FileName

Poniższy kod skonfigurował aplikację tak, aby sprawdzała katalog, z którego uruchomiona została aplikacja wykonywalna pojedynczo, zamiast miejsca, do którego rozpakowano pliki binarne.

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

GetBasePath()Realizacja:

private string GetBasePath()
{
    using var processModule = Process.GetCurrentProcess().MainModule;
    return Path.GetDirectoryName(processModule?.FileName);
}
Jason Yandell
źródło
Ta odpowiedź plus poniżej @ ronald-swaine jest idealna. Zestawy są wykluczone z dołączonego pliku exe, a zadanie publikowania umieszcza pliki zestawień obok dołączonego pliku exe.
Aaron Hudon
8

Jeśli nie masz nic przeciwko, aby pliki były używane w czasie wykonywania poza plikiem wykonywalnym, możesz po prostu oflagować pliki, które chcesz na zewnątrz, w csproj. Ta metoda pozwala na zmiany na żywo i takie w znanej lokalizacji.

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

Jeśli jest to niedopuszczalne i musi zawierać TYLKO jeden plik, przekazuję ścieżkę wyodrębnioną z jednego pliku jako ścieżkę katalogu głównego w konfiguracji hosta. Umożliwia to konfigurację i maszynkę do golenia (które dodam później), aby normalnie znajdować pliki.

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

Uwaga: aby naprawdę utworzyć pojedynczy plik i nie mieć PDB, potrzebujesz również:

<DebugType>None</DebugType>
Ronald Swaine
źródło
W jaki sposób Views \ Test.cshtml w twoim przykładzie dostaje się na komputer docelowy? Mam pliki ze zdjęciami i słownikami, które muszę otworzyć w oparciu o kulturę
Paul Cohen,
@PaulCohen Zazwyczaj wdrażam wszystkie pliki z opublikowanej lokalizacji wyjściowej przy użyciu SCP. Tylko ze zmianami w csproj (pierwszy przykład) interfejsy API korzystające z domyślnego głównego katalogu zawartości będą musiały mieć dostępne pliki w katalogu roboczym. Wygląda na to, że musisz użyć drugiego przykładu, aby uzyskać rzeczywistą ścieżkę do wyodrębnionej zawartości, aby w ten sposób uzyskać dostęp do obrazów z pełnej ścieżki, lub odpowiednio ustawiając katalog główny zawartości, aby ścieżki ~ / ... do obrazów można użyć w maszynce do golenia.
Ronald Swaine
Moje tło jest w aplikacjach osadzonych, więc pojedynczy plik binarny jest zwykle wypalany w ROM. Uświadamiam sobie, że istnieje Exe, które dzielę w jakimś samorozpakowującym się pliku „zip”. Wszystkie moje pliki danych są tam i po uruchomieniu aplikacji wszystko jest wyodrębniane bezpośrednio do temp. Jeśli stwierdzę, że mogę znaleźć moje pliki danych. Oznacza to również, że potrzebuję trochę logiki, aby móc debugować w VS, gdzie dane są gdzieś indziej.
Paul Cohen
1
Mam obecnie tę konfigurację i losowo przestała znajdować pliki.
Znikający