ASP.NET Coreアプリケーションでは、実行環境に関する情報をどのように設定して取得しますか?



HostingEnvironmentは、IHostingEnvironmentインタフェースを実装したすべてのタイプおよび対応するオブジェクトの総称である、現在の実行環境をベアラする記述である.次のコードフラグメントに示すように、1つのHostingEnvironmentオブジェクトがベアラする実行環境の記述情報は、このインタフェースを定義する6つの属性に反映される.ApplicationNameとEnvironmentNameは、それぞれ現在のアプリケーションの名前と実行環境の名前を表します.WebRootPathとContentRootPathは、外部からHTTPを介してアクセスを要求されるリソースを格納するために使用される2つのルートディレクトリへのパスであり、後者はアプリケーション自身の内部に必要なリソースを格納するために使用される.このインタフェースのContentRootFileProviderとWebRootFileProviderのプロパティは、両方のディレクトリに対するFileProviderオブジェクトを返します.以下に示すHostingEnvironmentタイプは、IHostingEnvironmentインタフェースのデフォルト実装である.[本文は『ASP.NET Coreフレームワークの秘密を明らかにする』に同期した]
   1: public interface IHostingEnvironment
   2: {
   3:     string         ApplicationName { get; set; }
   4:     string         EnvironmentName { get; set; }
   5:     IFileProvider  ContentRootFileProvider { get; set; }
   6:     string         ContentRootPath { get; set; }
   7:     IFileProvider  WebRootFileProvider { get; set; }
   8:     string         WebRootPath { get; set; }
   9: }
  10:
  11: public class HostingEnvironment : IHostingEnvironment
  12: {
  13:     string         ApplicationName { get; set; }
  14:     string         EnvironmentName { get; set; }
  15:     IFileProvider  ContentRootFileProvider { get; set; }
  16:     string         ContentRootPath { get; set; }
  17:     IFileProvider  WebRootFileProvider { get; set; }
  18:     string         WebRootPath { get; set; }
  19: }

一、アプリケーション環境


次に、HostingEnvironmentオブジェクトがベアラする実行環境記述情報のソースについて詳しく説明しますが、これまでに「Microsoft.Extensions.PlatformAbstractions」というNuGetパッケージに定義されているApplicationEnvironmentという別のタイプを理解する必要があります.そのネーミングからも、このオブジェクトが実行環境に関する情報を記述することが分かるが、これらの情報は、アプリケーションの名前、ベースパス、バージョン、および採用をそれぞれ表す4つの属性メンバーに記載されている.NET Framework.
   1: public class ApplicationEnvironment
   2: {
   3:     public string         ApplicationName {  get; }
   4:     public string         ApplicationBasePath {  get; }
   5:     public string         ApplicationVersion {  get; }
   6:     public FrameworkName  RuntimeFramework { get; }
   7: }

現在の実行環境を記述するためにApplicationEnvironmentオブジェクトを取得する必要がある場合は、PlatformServicesという名前のオブジェクトを使用する必要があります.そのApplicationプロパティは、必要なApplicationEnvironmentオブジェクトを返します.このタイプには共通の構造関数が存在しないため、PlatformServicesオブジェクトを直接インスタンス化することはできませんが、Defaultプロパティを使用してこの単一のオブジェクトを得ることができます.
   1: public class PlatformServices
   2: {
   3:     private PlatformServices();
   4:     public ApplicationEnvironment     Application { get; }
   5:     public static PlatformServices    Default { get; }
   6: }

ApplicationEnvironmentオブジェクトの場合、そのApplicationName、ApplicationVersion、およびRuntimeFrameworkのプロパティは、プログラムエントリMainメソッドを定義するプログラムセットによって決定され、具体的には、ApplicationNameおよびApplicationVersionは、それぞれこのプログラムセットの名前とバージョンを返し、このプログラムセットをコンパイルするために使用する.NET FrameworkのバージョンはRuntimeFrameworkプロパティに対応しています.ApplicationBasePathプロパティについては、実際にはAppContextのBaseDirectoryPathプロパティに対応するパスを返し、実行時にこのベースパスを使用してロードされたターゲット・プログラム・セットの実際のパスを解析します.この4つのプロパティの値は、次のプログラムで検証できます.
   1: public class Program
   2: {
   3:     public static void Main()
   4:     {
   5:         Assembly assembly = typeof(Program).GetTypeInfo().Assembly;
   6:         AssemblyName assemblyName = assembly.GetName();
   7:         ApplicationEnvironment env = PlatformServices.Default.Application;
   8:
   9:         Debug.Assert(env.ApplicationBasePath == AppContext.BaseDirectory);
  10:         Debug.Assert(env.ApplicationName == assemblyName.Name);
  11:         Debug.Assert(env.ApplicationVersion == assemblyName.Version.ToString());
  12:         Debug.Assert(env.RuntimeFramework.ToString() == assembly.GetCustomAttribute().FrameworkName);
  13:     }
  14: }

アプリケーション名を明示的に設定していない場合は、現在のHostingEnvironmentのApplicationNameプロパティが表すアプリケーション名は、このApplicationEnvironmentオブジェクトの同じ名前のプロパティに由来します.HostingEnvironmentには、ApplicationNameを含む4つのプロパティ(WebRootFileProviderおよびContentRootFileProviderのプロパティは含まれません.ContentRootPathおよびWebRootPathのプロパティに対応するためです)は、WebHostOptionsで設定できます.前章の説明では、WebHostOptionsオブジェクトはWebHostBuilderが採用した構成に基づいて作成されていることを知っていますので、構成によって実行環境を決定することができます.

二、コンフィギュレーションとWebHostOptions


HostingEnvironmentの4つのプロパティ(ApplicationName、EnvironmentName、WebRootPath、ContentRootPath)でロードされる4つの実行環境に関する設定については、WebHostOptionsオブジェクトに対応するプロパティがあり、後者は前者のデータソースです.WebHostOptionsオブジェクトは、WebHostBuilderが使用する構成に基づいて作成されるため、これらの設定は最初に使用する構成に由来します.なお、EnvironmentNameプロパティが明示的に設定されていない場合、デフォルト値はProductionです.
WebHostBuilderは環境変数を構成ソースとして採用し、「ASPNEtCORE_」を採用する環境変数フィルタとして使用されるプレフィックスとして、環境変数を設定することによって、HostingEnvironmentによってベアラされる実行環境オプションを完全に初期化することができます.
   1: Environment.SetEnvironmentVariable("ASPNETCORE_APPLICATIONNAME", "MyApp");
   2: Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Staging");
   3: Environment.SetEnvironmentVariable("ASPNETCORE_WEBROOT", @"c:\myapp\wwwroot\");
   4: Environment.SetEnvironmentVariable("ASPNETCORE_CONTENTROOT", @"c:\myapp\contentroot");
   5:
   6: new WebHostBuilder()
   7:     .UseConfiguration(new ConfigurationBuilder().AddJsonFile("weboptions.json"))
   8:     .ConfigureServices(svcs => {
   9:         IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService();
  10:         Debug.Assert(env.ApplicationName == "MyApp");
  11:         Debug.Assert(env.EnvironmentName == "Staging");
  12:         Debug.Assert(env.WebRootPath == @"c:\myapp\wwwroot\");
  13:         Debug.Assert(env.ContentRootPath == @"c:\myapp\contentroot");
  14:     })
  15:     .UseKestrel()
  16:     .Build();

WebHostBuilderではデフォルトで環境変数を構成ソースとして使用しますが、コンフィギュレーションオブジェクトを明示的に作成し、その拡張メソッドUseコンフィギュレーションを呼び出してインポートできます.上記のプログラムについて、構成を次のような構造を持つJSONファイル(weboptions.json)に定義する場合は、WebHostを作成する前に、UseConfigurationメソッドを呼び出して対応する構成をインポートするだけです.
weboptions.json:
   1: {
   2:   "applicationName": "MyApp",
   3:   "environment"    : "Staging",
   4:   "webRoot"        : "c:\\myapp\\wwwroot",
   5:   "contentRoot"    : "c:\\myapp\\contentroot"
   6: }

Program
   1: new WebHostBuilder()
   2:     .UseConfiguration(new ConfigurationBuilder().AddJsonFile("weboptions.json").Build())
   3:     .ConfigureServices(svcs => {
   4:         IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService();
   5:         Debug.Assert(env.ApplicationName  == "MyApp");
   6:         Debug.Assert(env.EnvironmentName  == "Staging");
   7:         Debug.Assert(env.WebRootPath       == @"c:\myapp\wwwroot\");
   8:         Debug.Assert(env.ContentRootPath  == @"c:\myapp\contentroot");
   9:     })
  10:     .UseKestrel()
  11:     .Build();

三、特殊なアプリケーションName


HostingEnvironmentの4つの属性では、アプリケーション名を表すApplicationNameが特殊です.初期値は構成に由来しますが、コンフィギュレーションメソッドまたはUseStartupメソッドを呼び出すと、このプロパティが上書きされます.WebHostを作成する前にConfigureメソッドを呼び出すと、環境変数設定のアプリケーション名(「MyApp」)が無効になります.
   1: Environment.SetEnvironmentVariable("ASPNETCORE_APPLICATIONNAME", "MyApp");
   2: new WebHostBuilder()
   3:     .ConfigureServices(svcs => {
   4:         IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService();
   5:         Debug.Assert(env.ApplicationName != "MyApp");
   6:     })
   7:     .UseKestrel()
   8:     .Configure(app => {})
   9: .Build();

実はこの問題の答えは『応用の入り口--Startup』ですでに与えられています.WebHostBuilderがStartupを登録するための2つの拡張メソッドConfigureとUseStartupの定義を以下に示します.Startupを作成して登録する前に、現在のアプリケーションの名前が設定されていることがわかります.
   1: public static class WebHostBuilderExtensions
   2: {
   3:     public static IWebHostBuilder Configure(this IWebHostBuilder hostBuilder, Action configureApp)
   4:     {
   5:         var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;
   6:
   7:         return hostBuilder
   8:             .UseSetting("applicationName", startupAssemblyName)
   9:             …
  10:     }
  11:
  12:     public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
  13:     {
  14:         var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
  15:         return hostBuilder
  16:             .UseSetting("ApplicationName", startupAssemblyName)
  17:             ...
  18:     }
  19: }

WebHostBuilderのUseStartupメソッドを呼び出して起動クラスを設定すると、このタイプが存在するプログラムセット名が現在のアプリケーションの名前になります.コンフィギュレーションメソッドを使用してアクションタイプの委任オブジェクトを指定した場合、この委任オブジェクト対応メソッドはどのタイプに定義され、このタイプが存在するプログラムベース名がアプリケーション名として使用されます.後者の場合、このActionオブジェクトを提供するには、最終的に設定されたアプリケーション名がまったく異なる2つの方法があります.
   1: public static class Startup
   2: {
   3:     public static void Configure(IApplicationBuilder app);
   4: }
   5:
   6: //Configure(app=>Startup.Configure(app))
   7: new WebHostBuilder()
   8:     .ConfigureServices(svcs => {
   9:         IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService();
  10:         Debug.Assert(env.ApplicationName == Assembly.GetEntryAssembly().GetName().Name);
  11:     })
  12:     .UseKestrel()
  13:     .Configure(app=>Startup.Configure(app))
  14:     .Build();
  15:
  16: //Configure(Startup.Configure)
  17: new WebHostBuilder()
  18:     .ConfigureServices(svcs => {
  19:         IHostingEnvironment env = svcs.BuildServiceProvider().GetRequiredService();
  20:         Debug.Assert(env.ApplicationName == typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
  21:     })
  22:     .UseKestrel()
  23:     .Configure(Startup.Configure)
  24:     .Build();