web-dev-qa-db-ja.com

.NET Core 2.0でオブジェクトグラフに構成をバインドする

.NET Core 2.0アプリを作成していて、設定する必要があります。私は このドキュメント を見ていて、.NET Core 1.0では次のことができるようです:

var appConfig = new AppSettings();
config.GetSection("App").Bind(appConfig);

.NET Core 1.1では、次のことができます。

var appConfig = config.GetSection("App").Get<AppSettings>();

ただし、.NET Core 2.0にはBindもGetもありません。これを達成するための新しい方法は何ですか?

おかげで、

ジョシュ

10
Joshua Walsh

これらの両方を実行できます。コンソールアプリケーションを使用しているため、ASP.NET Coreメタパッケージを使用していない可能性があるため、正しい依存関係があることを確認する必要があります。

構成をオブジェクトにバインドするには、Microsoft.Extensions.Configuration.Binderパッケージが必要です。そうすれば、どちらのソリューションも問題なく機能するはずです。


ところでコンソールアプリケーションを使用している場合でも、ASP.NET Coreに付属している依存関係注入コンテナーを利用できます。個人的には設定が非常に簡単だと思ったので、それでもアプリケーションを変更して使用できる場合は、それだけの価値があるかもしれません。セットアップは次のようになります。

var configuration = new ConfigurationBuilder()
    .AddJsonFile("config.json", optional: false)
    .Build();

var services = new ServiceCollection();
services.AddOptions();

// add your services here
services.AddTransient<MyService>();
services.AddTransient<Program>();

// configure options
services.Configure<AppSettings>(configuration.GetSection("App"));

// build service provider
var serviceProvider = services.BuildServiceProvider();

// retrieve main application instance and run the program
var program = serviceProvider.GetService<Program>();
program.Run();

次に、登録されているすべてのサービスは、ASP.NET Coreで行うのと同じように依存関係を取得できます。そして、構成を使用するために、通常のようにIOptions<AppSettings>タイプを注入できます。

8
poke

私が最終的にそれを理解するまで、私はまだこれで問題を抱えていました。

コードは問題なく実行されていましたが、バインド後もすべてのプロパティはnullのままでした。私はこれをしていました:

public class AppSettings
{
    public string MyProperty
}

そして、あなたはこれをしなければならないことがわかりました:

public class AppSettings
{
    public string MyProperty { get; set; }
}

これは、クラスにフィールドではなくプロパティがある場合にのみ機能します。これは私には分かりませんでした。

8
Joshua Walsh

Startup中に構成を登録する場合は、これをStartup.csに追加します。

services.Configure<AppSettings>(Configuration.GetSection("App"));

次に、IOptions<>のインスタンスを挿入することでアクセスできます。

private readonly AppSettings _appSettings;
public MyClass(IOptions<AppSettings> appSettings) {
    _appSettings = appSettings.Value;
}
1
JLe

構成を簡単にするために、入れ子になった構成の構成オブジェクトをスキャンするヘルパークラスを作成し、読み込まれたアセンブリで対応するクラスを見つけて、指定された構成で初期化しました。

appsettings.json:

{
    "MyState": {
        "SomeSimpleValue": "Hello World",
        "MyTimeSpan": "00:15:00"
    }
}

MyStateOptions.cs

// Class has same name as in appsettings.json with Options suffix.
public class MyStateOptions
{
    // Properties must be deserializable from string
    // or a class with a default constructor that has
    // only properties that are deserializable from string.
    public string SomeSimpleValue { get; set; }
    public DateTime MyTimeSpan { get; set; }
}

Startup.cs

public class Startup
{
    public IConfigurationRoot Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        // Create configuration as you need it...
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile(...)
            .AddEnvironmentVariables();

        // Save configuration in property to access it later.
        Configuration = builder.Build();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        // Register all your desired services...
        services.AddMvc(options => ...);

        // Call our helper method
        services.RegisterOptions(Configuration);
    }
}

HelperClass.cs

public static class IServiceCollectionExtensions
{
    public static void RegisterOptions(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        // Create all options from the given configuration.
        var options = OptionsHelper.CreateOptions(configuration);

        foreach (var option in options)
        {
            // We get back Options<MyOptionsType> : IOptions<MyOptionsType>
            var interfaces = option.GetType().GetInterfaces();

            foreach (var type in interfaces)
            {
                // Register options IServiceCollection
                services.AddSingleton(type, option);
            }
        }
    }
}

OptionsHelper.cs

public static class OptionsHelper
{
    public static IEnumerable<object> CreateOptions(IConfiguration configuration)
    {
        // Get all sections that are objects:
        var sections = configuration.GetChildren()
            .Where(section => section.GetChildren().Any());

       foreach (var section in sections)
       {
           // Add "Options" suffix if not done.
           var name = section.Key.EndsWith("Options")
               ? section.Key 
               : section.Key + "Options";
           // Scan AppDomain for a matching type.
           var type = FirstOrDefaultMatchingType(name);

           if (type != null)
           {
               // Use ConfigurationBinder to create an instance with the given data.
               var settings = section.Get(type);
               // Encapsulate instance in "Options<T>"
               var options = CreateOptionsFor(settings);
           }
       }
    }

    private static Type FirstOrDefaultMatchingType(string typeName)
    {
        // Find matching type that has a default constructor
        return AppDomain.CurrentDomain.GetAssemblies()
            .Where(Assembly => !Assembly.IsDynamic)
            .SelectMany(Assembly => Assembly.GetTypes())
            .Where(type => type.Name == typeName)
            .Where(type => !type.IsAbstract)
            .Where(type => type.GetMatchingConstructor(Type.EmptyTypes) != null)
            .FirstOrDefault();
    }

    private static object CreateOptionsFor(object settings)
    {
        // Call generic method Options.Create<TOptions>(TOptions options)
        var openGeneric = typeof(Options).GetMethod(nameof(Options.Create));
        var method = openGeneric.MakeGenericMethod(settings.GetType());

        return method.Invoke(null, new[] { settings });
    }
}

これらすべてを行った後、サービスコレクション内に、コンストラクターでIOptions<MyStateOptions>を要求するサービスを設定できます。各オプションを明示的に構成しなくても、サービスを取得できます。必要なサービスとオプションのインスタンスで新しいプロジェクトを作成するだけです。プロジェクトをメインプロジェクトに追加し、必要な構成をappsettings.jsonに追加します。

ExampleService.cs

public class MyExampleService
{
    private readonly MyStateOptions _options;

    public MyExampleService(IOptions<MyStateOptions> options)
    {
        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
    }
}
0
Oliver

これは、設定オブジェクトをバインドし、.Net Core 3.0でシングルトンとして追加する方法です

public void ConfigureServices(IServiceCollection services)
        {
            var jwtSettings = new JwtSettings();
            Configuration.Bind(jwtSettings);
            services.AddSingleton(jwtSettings);

            var databaseSettings = new DatabaseSettings();
            Configuration.Bind(databaseSettings);
            services.AddSingleton(databaseSettings);


            services.AddControllersWithViews();
        }

私の設定オブジェクトは次のようになります:

public class DatabaseSettings
    {
        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }
    }

public class JwtSettings
    {
        public string Secret { get; set; }
        public string Lifetime { get; set; }
    }

私のappsettings.jsonファイルは次のようになります。

{
  "DatabaseSettings": {
    "ConnectionString": "mongodb://localhost:27017",
    "DatabaseName": "TestDb"
  },
  "JwtSettings": {
    "Secret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "Lifetime": "170"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
0
brainoverflow98