Aracılığıyla paylaş


.NET kitaplığı yazarları için Options yapı deseni kılavuzu

Bağımlılık enjeksiyonu sayesinde, hizmetlerinizi ve bunlara ait yapılandırmaları kaydetmek için options pattern kullanılabilir. Seçenekler düzeni, kitaplığınızın (ve hizmetlerinizin) tüketicilerinin seçenekler sınıfınız olan TOptions örneklerini gerektirmesini sağlar. Yapılandırma seçeneklerini güçlü türdeki nesneler aracılığıyla tüketmek, tutarlı değer gösterimi sağlamaya yardımcı olur, veri ek açıklamalarıyla doğrulamayı mümkün kılar ve dize değerlerini elle ayrıştırma yükünü ortadan kaldırır. Kitaplığınızın tüketicilerinin kullanabileceği birçok yapılandırma sağlayıcısı vardır. Bu sağlayıcılarla tüketiciler kitaplığınızı birçok yolla yapılandırabilir.

Bir .NET kitaplığı yazarı olarak, seçenekler desenini kitaplığınızın tüketicilerine doğru şekilde gösterme hakkında genel yönergeleri öğreneceksiniz. Aynı şeyi başarmanın çeşitli yolları ve dikkat edilmesi gereken birkaç nokta vardır.

Adlandırma kuralları

Kural gereği, hizmetleri kaydetmekle sorumlu uzantı yöntemleri olarak adlandırılır Add{Service}; burada {Service} anlamlı ve açıklayıcı bir addır. Add{Service} uzantı yöntemleri hem ASP.NET Core hem de .NET'te yaygındır.

✔️ Hizmetinizi diğer hizmetlerden ayıran adları düşünün.

❌ Resmi Microsoft paketlerinden .NET ekosisteminin parçası olan adları KULLANMAYIN.

✔️ Uzantı yöntemlerini kullanıma sunan statik sınıfları olarak {Type}Extensionsadlandırmayı düşünün. Burada {Type} , genişlettiğiniz türdür.

Namespace rehberi

Microsoft paketleri, çeşitli hizmet tekliflerinin kaydını birleştirmek için Microsoft.Extensions.DependencyInjection ad alanını kullanır.

✔️ Paket teklifinizi net bir şekilde tanımlayan bir ad alanı DÜŞÜNÜN.

❌ Ad alanını Microsoft.Extensions.DependencyInjection resmi olmayan Microsoft paketleri için KULLANMAYIN.

Parametresiz

Hizmetiniz çok az açık yapılandırmayla çalışabiliyorsa veya hiç açık yapılandırmayla çalışamıyorsa parametresiz bir uzantı yöntemi kullanmayı göz önünde bulundurun.

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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

IConfiguration parametresi

Birçok seçeneği tüketicilere sunan bir kitaplık yazarken, bir IConfiguration parametre uzantısı yöntemi gerektirmeyi düşünebilirsiniz. Beklenen IConfiguration örneği, IConfiguration.GetSection işlevi kullanılarak yapılandırmanın adlandırılmış bir bölümüne atanmalıdır.

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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

Bu desendeki tüketiciler, adlandırılmış bölümün kapsamlı IConfiguration örneğini sağlar:

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();

Çağrı .AddMyLibraryService türü üzerinde IServiceCollection yapılır.

Kitaplık yazarı olarak varsayılan değerlerin belirtilmesi size bağlı.

Uyarı

Yapılandırmayı bir seçenek örneğine bağlamak mümkündür. Ancak ad çakışması riski vardır ve bu da hatalara neden olur. Buna ek olarak, bu şekilde el ile bağlandığınızda, seçenek deseninizin tüketimini yalnızca bir kez okumakla sınırlandırırsınız. Bu tür tüketiciler IOptionsMonitor arabirimini kullanamayacağından, ayarlarda yapılan değişiklikler yeniden bağlanmaz.

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

Bunun yerine uzantı yöntemini kullanmanız BindConfiguration gerekir. Bu uzantı yöntemi, yapılandırmayı seçenekler örneğine bağlar ve yapılandırma bölümü için bir değişiklik belirteci kaynağı kaydeder. Bu, tüketicilerin IOptionsMonitor arabirimini kullanmasına olanak tanır.

Yapılandırma bölümü yol parametresi

Kitaplığınızı kullananlar, temel TOptions türünü bağlamak için yapılandırma bölümü yolunu belirtmek isteyebilir. Bu senaryoda, uzantı yönteminizde bir string parametre tanımlarsınız.

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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

Sonraki örnekte, veri ek açıklaması doğrulamasını etkinleştirmek için Microsoft.Extensions.Options.DataAnnotations NuGet paketi kullanılır. SupportOptions sınıfı aşağıdaki gibi tanımlanır:

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; }
}

Aşağıdaki JSON appsettings.json dosyasının kullanıldığını düşünün:

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

Action<TOptions> parametresi

Kitaplığınızın tüketicileri, seçenekler sınıfınızın bir örneğini veren bir lambda ifadesi sağlamak isteyebilir. Bu senaryoda, uzantı yönteminizde bir Action<LibraryOptions> parametre tanımlarsınız.

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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

  • IServiceCollection örneğini genişletir
  • Action<T> parametresini configureOptions, T'nin LibraryOptions olduğu yer tanımlar
  • Configure belirtilen configureOptions eylemini çağırır

Bu desendeki tüketiciler, bir lambda ifadesi (veya Action<LibraryOptions> parametresini karşılayan bir temsilci) sağlar:

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();

Seçenekler nesnesi parametresi

Kütüphanenizin kullanıcıları, gömülü ayarlar örneği sağlamayı tercih edebilir. Bu senaryoda, options nesnenizin LibraryOptionsörneğini alan bir uzantı yöntemini kullanıma sunarsınız.

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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

Bu desen içindeki tüketiciler, istenen özellik değerlerini satır içinde tanımlayarak LibraryOptions sınıfının bir örneğini sağlar.

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();

Sonraki yapılandırma

Tüm yapılandırma seçeneği değerleri bağlandıktan veya belirtildikten sonra, yapılandırma sonrası işlevselliği kullanılabilir. Daha önce ayrıntılı olarak belirtilen aynı Action<TOptions> parametreyi kullanmayı seçebilirsiniz PostConfigure. Yapılandırma sonrası tüm .Configure çağrıların ardından çalıştırılır. PostConfigure kullanmayı düşünmek isteyebileceğiniz birkaç neden vardır:

  • Yürütme sırası: Çağrılarda .Configure ayarlanan tüm yapılandırma değerlerini geçersiz kılabilirsiniz.
  • Doğrulama: Diğer tüm yapılandırmalar uygulandıktan sonra varsayılan değerlerin ayarlandığını doğrulayabilirsiniz.
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;
    }
}

Yukarıdaki kodda, :AddMyLibraryService

Bu düzendeki tüketiciler, post olmayan bir yapılandırma senaryosunda olduğu gibi bir lambda ifadesi (veya parametreyi Action<LibraryOptions> karşılayan Action<TOptions> bir temsilci) sağlar:

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();

Ayrıca bakınız