.settings
ファイルに依存する.NETコアにアプリケーションを移植しています。残念ながら、.NETコアからそれを読み取る方法が見つかりません。通常、次の行を.csproj
に追加すると、TestSettings
クラスが生成され、設定を読み取ることができます。
<ItemGroup>
<None Include="TestSettings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
</None>
</ItemGroup>
残念ながら、これはもはや何もしないようです。 SettingsSingleFileGenerator
が実行されていることを確認することもできません。この GitHubの問題 は、これが新しい.csproj
形式のバグであることを示していますが、代替案を提供している人はいません。
.NETコアで.settings
ファイルを読み取る適切な方法は何ですか?
.NET Core 2.xの場合、Microsoft.Extensions.Configuration
名前空間(下記の注を参照)。環境変数からAzure Key Vaultまでのソース(ただし、より現実的には、JSONファイル、XMLなど)から読み取るために取得したいNuGetの拡張機能がたくさんあります。
KestrelがAzureサイトで起動するときに設定を使用するのと同じ方法で設定を取得するコンソールプログラムの例を次に示します。
public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
// This allows us to set a system environment variable to Development
// when running a compiled Release build on a local workstation, so we don't
// have to alter our real production appsettings file for compiled-local-test.
//.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
//.AddAzureKeyVault()
.Build();
次に、設定が必要なコードで、Configuration
を参照するか、依存関係注入などのためにIConfiguration
を登録します。
注:IConfiguration
は読み取り専用であり、 this コメントごとに永続化されることはありません。したがって、読み取りと書き込みが必要な場合は、別のオプションが必要になります。多分 System.Configuration
sansデザイナー。
既存のプロジェクトを移植するとき、私は通常、生成されたSettings.Designer.csを古いプロジェクトから新しいプロジェクトにコピーします。しかし、これは設定ファイルの変更や新しい設定キーの追加には適していません。また、.net-Framework-Settingsの場合と異なり、新しいバージョンをインストールした後、ユーザーの設定が削除されていることにも気付きました。
私が質問で尋ねたように、これが「適切」である方法はありませんが、より妥当なものが来るまで、これを一時的なギャップとして使用しています。私はそれが他の人のために働くことを保証できません。
.settings
ファイルを埋め込みリソースとして含め、次のように使用します。
private static readonly ConfigurationShim Configuration = new ConfigurationShim("MyApp.Settings.settings");
public static bool MyBoolSetting => (bool) Configuration["MyBoolSetting"];
コード:
internal class ConfigurationShim
{
private static readonly XNamespace ns = "http://schemas.Microsoft.com/VisualStudio/2004/01/settings";
private readonly Lazy<IDictionary<string, object>> configuration;
public ConfigurationShim(string settingsResourceName)
{
configuration = new Lazy<IDictionary<string, object>>(
() =>
{
Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream stream = Assembly.GetManifestResourceStream(settingsResourceName))
using (var reader = new StreamReader(stream))
{
XDocument document = XDocument.Load(reader);
return document.Element(ns + "SettingsFile")
.Element(ns + "Settings")
.Elements(ns + "Setting")
.Select(ParseSetting)
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
}
});
}
public object this[string property] => configuration.Value[property];
private static (string, object) ParseSetting(XElement setting)
{
string name = setting.Attribute("Name").Value;
string typeName = setting.Attribute("Type").Value;
string value = setting.Element(ns + "Value").Value;
Type type = Type.GetType(typeName);
IEnumerable<ConstructorInfo> ctors = GetSuitableConstructors(type);
IEnumerable<MethodInfo> staticMethods = GetSuitableStaticMethods(type);
object obj = null;
foreach (MethodBase method in ctors.Cast<MethodBase>().Concat(staticMethods))
{
try
{
obj = method.Invoke(null, new object[] {value});
break;
}
catch (TargetInvocationException)
{
// ignore and try next alternative
}
}
return (name, obj);
}
private static IEnumerable<MethodInfo> GetSuitableStaticMethods(Type type)
{
// To use a static method to construct a type, it must provide a method that
// returns a subtype of itself and that method must take a single string as
// an argument. It cannot be generic.
return type.GetMethods().Where(method =>
{
ParameterInfo[] parameters = method.GetParameters();
return !method.ContainsGenericParameters &&
method.IsStatic &&
parameters.Length == 1 &&
parameters[0].ParameterType.IsAssignableFrom(typeof(string)) &&
type.IsAssignableFrom(method.ReturnType);
});
}
private static IEnumerable<ConstructorInfo> GetSuitableConstructors(Type type)
{
// We need a constructor of a single string parameter with no generics.
return type.GetConstructors().Where(ctor =>
{
ParameterInfo[] parameters = ctor.GetParameters();
return !ctor.ContainsGenericParameters &&
parameters.Length == 1 &&
parameters[0].ParameterType.IsAssignableFrom(typeof(string));
});
}
}