次の方法で共有


.NET ライブラリ作成者向けのオプション パターン ガイダンス

依存関係の挿入の助けを借りて、サービスとそれに対応する構成を登録することで、 オプション パターンを利用できます。 オプション パターンを使用すると、ライブラリ (およびサービス) のコンシューマーはTOptionsのインスタンスを要求できます。 厳密に型指定されたオブジェクトを使用して構成オプションを使用すると、一貫性のある値表現を確保し、データ注釈を使用した検証を可能にし、文字列値を手動で解析する手間を省きます。 ライブラリのコンシューマーが使用する 構成プロバイダー は多数あります。 これらのプロバイダーを使用すると、コンシューマーはさまざまな方法でライブラリを構成できます。

.NET ライブラリ作成者として、あなたはライブラリの利用者にオプション パターンを正しく公開するための一般的な指針を学びます。 同じことを実現するにはさまざまな方法があり、いくつかの考慮事項を考慮する必要があります。

名前付け規則

慣例により、サービスの登録を担当する拡張メソッドの名前は Add{Service} で、 {Service} はわかりやすい名前です。 Add{Service} 拡張メソッドは、 ASP.NET Core でも .NET でも一般的です。

サービスを他の製品やサービスから区別する名前を検討してください。

❌ 公式の Microsoft パッケージの .NET エコシステムに既に含まれている名前は使用しないでください。

✔️ 拡張メソッドを {Type}Extensionsとして公開する静的クラスの名前付けについて検討してください。ここで、 {Type} は拡張する型です。

名前空間のガイドライン

Microsoft パッケージでは、 Microsoft.Extensions.DependencyInjection 名前空間を使用して、さまざまなサービス オファリングの登録を統合します。

✔️ パッケージオファリングを明確に識別する名前空間を検討してください。

❌ 公式ではない Microsoft パッケージには、 Microsoft.Extensions.DependencyInjection 名前空間を使用しないでください。

パラメータなし

サービスが最小限の構成で動作するか、明示的な構成を使用できない場合は、パラメーターなしの拡張メソッドを検討してください。

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
        this IServiceCollection services)
    {
        services.AddOptions<LibraryOptions>()
            .Configure(options =>
            {
                // Specify default option values
            });

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

IConfiguration パラメーター

多数のオプションをコンシューマーに公開するライブラリを作成する場合は、 IConfiguration パラメーター拡張メソッドを必要とすることを検討してください。 IConfiguration関数を使用して、予想されるIConfiguration.GetSection インスタンスのスコープを構成の名前付きセクションに設定する必要があります。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
      this IServiceCollection services,
      IConfiguration namedConfigurationSection)
    {
        // Default library options are overridden
        // by bound configuration values.
        services.Configure<LibraryOptions>(namedConfigurationSection);

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

このパターンのコンシューマーは、名前付きセクションのスコープ付き IConfiguration インスタンスを提供します。

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(
    builder.Configuration.GetSection("LibraryOptions"));

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

.AddMyLibraryServiceの呼び出しは、IServiceCollection型で行われます。

ライブラリの作成者は、既定値を指定する必要があります。

構成をオプション インスタンスにバインドできます。 ただし、名前の競合のリスクがあり、エラーが発生します。 さらに、この方法で手動でバインドする場合は、選択パターンの消費が1回の読み取りに制限されます。 このようなコンシューマーは IOptionsMonitor インターフェイスを使用できないため、設定の変更は再バインドされません。

services.AddOptions<LibraryOptions>()
    .Configure<IConfiguration>(
        (options, configuration) =>
            configuration.GetSection("LibraryOptions").Bind(options));

代わりに、 BindConfiguration 拡張メソッドを使用する必要があります。 この拡張メソッドは、構成をオプション インスタンスにバインドし、構成セクションの変更トークン ソースも登録します。 これにより、コンシューマーは IOptionsMonitor インターフェイスを 使用できます。

構成セクションのパス パラメーター

ライブラリの利用者は、根底にある TOptions 型をバインドするための構成セクションパスを指定できます。 このシナリオでは、拡張メソッドで string パラメーターを定義します。

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
      this IServiceCollection services,
      string configSectionPath)
    {
        services.AddOptions<SupportOptions>()
            .BindConfiguration(configSectionPath)
            .ValidateDataAnnotations()
            .ValidateOnStart();

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

  • のインスタンスを拡張します。 IServiceCollection
  • string パラメーターを定義しますconfigSectionPath
  • 呼び出し:
    • AddOptions のジェネリック型パラメーターを使用して SupportOptions を呼び出します
    • BindConfiguration 指定された configSectionPath パラメーターを使用する
    • ValidateDataAnnotations データ注釈の検証を有効にする
    • ValidateOnStart 実行時ではなく起動時に検証を適用する場合

次の例では、 Microsoft.Extensions.Options.DataAnnotations NuGet パッケージを使用して、データ注釈の検証を有効にします。 SupportOptions クラスは次のように定義されます。

using System.ComponentModel.DataAnnotations;

public sealed class SupportOptions
{
    [Url]
    public string? Url { get; set; }

    [Required, EmailAddress]
    public required string Email { get; set; }

    [Required, DataType(DataType.PhoneNumber)]
    public required string PhoneNumber { get; set; }
}

次の JSON appsettings.json ファイルが使用されるとします。

{
    "Support": {
        "Url": "https://support.example.com",
        "Email": "help@support.example.com",
        "PhoneNumber": "+1(888)-SUPPORT"
    }
}

Action<TOptions> パラメーター

ライブラリのコンシューマーは、options クラスのインスタンスを生成するラムダ式を提供することに関心がある場合があります。 このシナリオでは、拡張メソッドで Action<LibraryOptions> パラメーターを定義します。

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
        this IServiceCollection services,
        Action<LibraryOptions> configureOptions)
    {
        services.Configure(configureOptions);

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

  • のインスタンスを拡張します。 IServiceCollection
  • Action<T> のパラメーター configureOptions を定義します。ここで、TLibraryOptions です
  • Configure アクションを指定して configureOptions を呼び出します

このパターンのコンシューマーは、ラムダ式 (または Action<LibraryOptions> パラメーターを満たすデリゲート) を提供します。

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(options =>
{
    // User defined option values
    // options.SomePropertyValue = ...
});
                                                                        
using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

オプションインスタンスパラメーター

ライブラリのコンシューマーは、インライン オプション インスタンスを提供することを好む場合があります。 このシナリオでは、options オブジェクトのインスタンスを受け取る拡張メソッド ( LibraryOptions) を公開します。

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
      this IServiceCollection services,
      LibraryOptions userOptions)
    {
        services.AddOptions<LibraryOptions>()
            .Configure(options =>
            {
                // Overwrite default option values
                // with the user provided options.
                // options.SomeValue = userOptions.SomeValue;
            });

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

このパターンのコンシューマーは、 LibraryOptions クラスのインスタンスを提供し、必要なプロパティ値をインラインで定義します。

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(new LibraryOptions
{
    // Specify option values
    // SomePropertyValue = ...
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

構成後

すべての構成オプション値がバインドまたは指定されると、構成後の機能を使用できるようになります。 前に詳しく説明したのと同じAction<TOptions> パラメーターを公開して、PostConfigureを呼び出す場合があります。 事後構成は、.Configure のすべての呼び出しの後に実行されます。 PostConfigureの使用を検討する理由がいくつかあります。

  • 実行順序: .Configure 呼び出しで設定されたすべての構成値をオーバーライドできます。
  • 検証: 他のすべての構成が適用された後で、既定値が設定されていることを検証できます。
using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMyLibraryService(
      this IServiceCollection services,
      Action<LibraryOptions> configureOptions)
    {
        services.PostConfigure(configureOptions);

        // Register lib services here...
        // services.AddScoped<ILibraryService, DefaultLibraryService>();

        return services;
    }
}

上のコードの AddMyLibraryService では、次のことが行われています。

  • のインスタンスを拡張します。 IServiceCollection
  • Action<T> のパラメーター configureOptions を定義します。ここで、TLibraryOptions です
  • PostConfigure アクションを指定して configureOptions を呼び出します

このパターンのコンシューマーは、非事後構成シナリオの Action<LibraryOptions> パラメーターと同様に、ラムダ式 (または Action<TOptions> パラメーターを満たすデリゲート) を提供します。

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(options =>
{
    // Specify option values
    // options.SomePropertyValue = ...
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

こちらも参照ください