Dela via


Vägledning för alternativmönster för .NET-biblioteksförfattare

Med hjälp av beroendeinmatning kan du använda alternativmönstret för att registrera dina tjänster och deras motsvarande konfigurationer. Med alternativmönstret kan användare av biblioteket (och dina tjänster) kräva instanser av alternativgränssnitt där TOptions är din alternativklass. Om du använder konfigurationsalternativ via starkt skrivna objekt kan du säkerställa konsekvent värderepresentation, aktivera validering med dataanteckningar och ta bort belastningen för att parsa strängvärden manuellt. Det finns många konfigurationsprovidrar som användarna av biblioteket kan använda. Med dessa leverantörer kan konsumenter konfigurera biblioteket på många sätt.

Som .NET-biblioteksförfattare får du allmän vägledning om hur du korrekt exponerar alternativmönstret för användare av biblioteket. Det finns olika sätt att uppnå samma sak, och flera överväganden att göra.

Namngivningskonventioner

Enligt konventionen heter Add{Service}tilläggsmetoder som ansvarar för att registrera tjänster , där {Service} är ett meningsfullt och beskrivande namn. Add{Service} tilläggsmetoder är vanliga i både ASP.NET Core och .NET.

✔️ ÖVERVÄG namn som särskiljer din tjänst från andra erbjudanden.

❌ Använd INTE namn som redan ingår i .NET-ekosystemet från officiella Microsoft-paket.

✔️ ÖVERVÄG att namnge statiska klasser som exponerar tilläggsmetoder som {Type}Extensions, där {Type} är den typ som du utökar.

Vägledning för namnområde

Microsoft-paket använder Microsoft.Extensions.DependencyInjection namnområdet för att förena registreringen av olika tjänsterbjudanden.

✔️ ÖVERVÄG ett namnområde som tydligt identifierar paketerbjudandet.

❌ Använd Microsoft.Extensions.DependencyInjection INTE namnområdet för icke-officiella Microsoft-paket.

Parameterlös

Om tjänsten kan fungera med minimal eller ingen explicit konfiguration bör du överväga en metod för parameterlöst tillägg.

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

I föregående kod:AddMyLibraryService

IConfiguration Parametern

När du skapar ett bibliotek som exponerar många alternativ för konsumenter kanske du vill överväga att kräva en IConfiguration metod för parametertillägg. Den förväntade IConfiguration instansen bör begränsas till ett namngivet avsnitt i konfigurationen med hjälp IConfiguration.GetSection av funktionen .

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

I föregående kod:AddMyLibraryService

Konsumenter i det här mönstret tillhandahåller den begränsade IConfiguration instansen av det namngivna avsnittet:

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

Anropet till .AddMyLibraryService görs på IServiceCollection typen .

Som biblioteksförfattare är det upp till dig att ange standardvärden.

Kommentar

Det går att binda konfigurationen till en alternativinstans. Det finns dock en risk för namnkollisioner - vilket kommer att orsaka fel. När du manuellt binder på det här sättet begränsar du dessutom förbrukningen av alternativmönstret till att läsa en gång. Ändringar i inställningarna kommer inte att bindas på nytt, eftersom sådana konsumenter inte kommer att kunna använda gränssnittet IOptionsMonitor .

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

I stället bör du använda BindConfiguration tilläggsmetoden. Den här tilläggsmetoden binder konfigurationen till alternativinstansen och registrerar även en ändringstokenkälla för konfigurationsavsnittet. Detta gör att konsumenterna kan använda gränssnittet IOptionsMonitor .

Sökvägsparameter för konfigurationsavsnitt

Användare av biblioteket kanske vill ange konfigurationsavsnittets sökväg för att binda din underliggande TOptions typ. I det här scenariot definierar du en string parameter i tilläggsmetoden.

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

I föregående kod:AddMyLibraryService

I nästa exempel används NuGet-paketet Microsoft.Extensions.Options.DataAnnotations för att aktivera validering av dataanteckning. Klassen SupportOptions definieras på följande sätt:

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

Anta att följande JSON-appsettings.json fil används:

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

Action<TOptions> Parametern

Användare av biblioteket kan vara intresserade av att tillhandahålla ett lambda-uttryck som ger en instans av din alternativklass. I det här scenariot definierar du en Action<LibraryOptions> parameter i tilläggsmetoden.

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

I föregående kod:AddMyLibraryService

  • Utökar en instans av IServiceCollection
  • Definierar en Action<T> parameter configureOptions där T är LibraryOptions
  • Anrop Configure som har angetts för configureOptions åtgärden

Konsumenter i det här mönstret tillhandahåller ett lambda-uttryck (eller ett ombud som uppfyller parametern 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();

Alternativinstansparameter

Användare av biblioteket kanske föredrar att ange en instans av infogade alternativ. I det här scenariot exponerar du en tilläggsmetod som tar en instans av ditt alternativobjekt, 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;
    }
}

I föregående kod:AddMyLibraryService

Konsumenter i det här mönstret tillhandahåller en instans av LibraryOptions klassen som definierar önskade egenskapsvärden infogade:

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

Efter konfiguration

När alla konfigurationsalternativvärden är bundna eller angivna är postkonfigurationsfunktioner tillgängliga. Om du exponerar samma Action<TOptions> parameter som beskrivs tidigare kan du välja att anropa PostConfigure. Efter konfigurering körs efter alla .Configure anrop. Det finns några anledningar till varför du vill överväga att använda PostConfigure:

  • Körningsordning: Du kan åsidosätta alla konfigurationsvärden som har angetts i anropen .Configure .
  • Validering: Du kan verifiera att standardvärdena har angetts när alla andra konfigurationer har tillämpats.
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;
    }
}

I föregående kod:AddMyLibraryService

Konsumenter i det här mönstret tillhandahåller ett lambda-uttryck (eller ett ombud som uppfyller parameternAction<LibraryOptions>), precis som med parametern Action<TOptions> i ett icke-postkonfigurationsscenario:

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

Se även