.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

上記のすべての方法は、ファイルの解凍先を指します。これは、実行された場所とは異なります。

回答:


14

ここのタイトルの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は完璧です。appsettingsはバンドルされたexeから除外され、公開タスクはappsettingsファイルをバンドルされた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>

これが受け入れられず、ファイルが1つだけでなければならない場合は、ホストセットアップで、単一ファイル抽出パスをルートパスとして渡します。これにより、構成とかみそり(後で追加)が通常どおりファイルを見つけることができます。

// 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はどのようにターゲットコンピュータに取得しますか?カルチャーに基づいて開く必要がある画像ファイルと辞書ファイルがあります。
Paul Cohen

@PaulCohen私は通常、SCPを使用して公開された出力場所からすべてのファイルを展開します。csproj(最初の例)の変更のみで、デフォルトのルートコンテンツディレクトリを使用しているすべてのAPIは、作業ディレクトリでファイルを使用できるようにする必要があります。2番目の例を使用して、抽出されたコンテンツの実際のパスを取得し、フルパスから画像にアクセスできるようにするか、コンテンツのルートを正しく設定して〜/ ...パスを設定する必要があるようですかみそりで画像を使用することができます。
ロナルドスウェイン

私の経歴は組み込みアプリケーションであるため、通常、単一のバイナリがROMに書き込まれます。私が理解しているのは、ある種の自己解凍型「zip形式」ファイルで共有するExeです。すべてのデータファイルがそこにあり、アプリケーションが実行されると、すべてが一時ファイルに直接抽出されます。データファイルを見つけることができる場合。また、データがどこかにあるVSでデバッグできるように、いくつかのロジックが必要であることも意味します。
ポールコーエン

1
私は現在この設定をしており、ファイルの検索をランダムに停止しました。
行く-なくなって

1

私のアプリケーションは.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>();
    });
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.