Delen via


Richtlijnen voor opties voor .NET-bibliotheekauteurs

Met behulp van afhankelijkheidsinjectie kan het registreren van uw services en de bijbehorende configuraties gebruikmaken van het optiespatroon. Met het optiespatroon kunnen gebruikers van uw bibliotheek (en uw services) exemplaren van optiesinterfaces vereisen, waarbij TOptions uw optiesklasse is. Het gebruik van configuratieopties via sterk getypte objecten helpt bij het garanderen van consistente waardeweergave, maakt validatie mogelijk met gegevensannotaties en verwijdert de last van het handmatig parseren van tekenreekswaarden. Er zijn veel configuratieproviders die gebruikers van uw bibliotheek kunnen gebruiken. Met deze providers kunnen consumenten uw bibliotheek op veel manieren configureren.

Als auteur van een .NET-bibliotheek leert u algemene richtlijnen voor het correct beschikbaar maken van het optiespatroon voor consumenten van uw bibliotheek. Er zijn verschillende manieren om hetzelfde te bereiken, en verschillende overwegingen die u moet maken.

Naamgevingsconventies

Conventioneel worden extensiemethoden die verantwoordelijk zijn voor het registreren van services benoemd Add{Service}, waar {Service} een betekenisvolle en beschrijvende naam is. Add{Service} extensiemethoden zijn gebruikelijk in ASP.NET Core en .NET.

✔️ HOUD REKENING met namen die uw service onderscheiden van andere aanbiedingen.

❌ GEBRUIK GEEN namen die al deel uitmaken van het .NET-ecosysteem van officiële Microsoft-pakketten.

✔️ OVERWEEG statische klassen te benoemen die extensiemethoden beschikbaar maken als {Type}Extensions, waar {Type} is het type dat u uitbreidt.

Richtlijnen voor namespaces

Microsoft-pakketten maken gebruik van de Microsoft.Extensions.DependencyInjection naamruimte om de registratie van verschillende serviceaanbiedingen te samenvoegen.

✔️ OVERWEEG een naamruimte die uw pakketaanbod duidelijk identificeert.

❌ GEBRUIK DE Microsoft.Extensions.DependencyInjection naamruimte NIET voor niet-officiële Microsoft-pakketten.

Parameterloos

Als uw service kan werken met minimale of geen expliciete configuratie, kunt u een parameterloze extensiemethode overwegen.

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

In de voorgaande code is de AddMyLibraryService:

IConfiguration parameter

Wanneer u een bibliotheek maakt die veel opties beschikbaar maakt voor consumenten, kunt u overwegen om een IConfiguration parameterextensiemethode te vereisen. Het verwachte IConfiguration exemplaar moet worden afgestemd op een benoemde sectie van de configuratie met behulp van de IConfiguration.GetSection functie.

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

In de voorgaande code is de AddMyLibraryService:

Consumenten via dit patroon gebruiken de gescope IConfiguration exemplaar van de benoemde sectie.

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

De aanroep van .AddMyLibraryService wordt gedaan op het IServiceCollection type.

Als auteur van de bibliotheek kunt u standaardwaarden opgeven.

Opmerking

Het is mogelijk om de configuratie te binden aan een exemplaar van opties. Er is echter een risico op naamconflicten, wat fouten veroorzaakt. Als u op deze manier handmatig bindingen gebruikt, beperkt u bovendien het verbruik van uw optiespatroon tot een keer lezen. Wijzigingen in instellingen worden niet opnieuw gebonden, omdat consumenten de IOptionsMonitor-interface niet kunnen gebruiken.

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

In plaats daarvan moet u de BindConfiguration extensiemethode gebruiken. Deze extensiemethode verbindt de configuratie met het exemplaar van opties en registreert ook een wijzigingstokenbron voor de configuratiesectie. Hierdoor kunnen consumenten de IOptionsMonitor-interface gebruiken.

Configuratiesectiepadparameter

Consumenten van uw bibliotheek willen mogelijk het pad naar de configuratiesectie opgeven om uw onderliggende TOptions type te binden. In dit scenario definieert u een string parameter in uw extensiemethode.

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

In de voorgaande code is de AddMyLibraryService:

In het volgende voorbeeld wordt het NuGet-pakket Microsoft.Extensions.Options.DataAnnotations gebruikt om validatie van gegevensaantekeningen in te schakelen. De SupportOptions klasse wordt als volgt gedefinieerd:

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

Stel dat het volgende JSON -appsettings.json-bestand wordt gebruikt:

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

Action<TOptions> parameter

Consumenten van uw bibliotheek zijn mogelijk geïnteresseerd in het leveren van een lambda-expressie die een exemplaar van uw optiesklasse oplevert. In dit scenario definieert u een Action<LibraryOptions> parameter in uw extensiemethode.

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

In de voorgaande code is de AddMyLibraryService:

  • Breidt een instantie van IServiceCollection uit
  • Definieert een Action<T> parameter configureOptions waar T is LibraryOptions
  • Aanroepen die de Configure actie hebben gekregen configureOptions

Gebruikers in dit patroon bieden een lambda-uitdrukking (of een delegate die voldoet aan de eisen van de Action<LibraryOptions> parameter):

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

Parameter voor optie-instantie

Gebruikers van uw bibliotheek geven mogelijk de voorkeur aan het leveren van een inline-optie-exemplaar. In dit scenario maakt u een extensiemethode beschikbaar die een exemplaar van uw optieobject gebruikt. 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;
    }
}

In de voorgaande code is de AddMyLibraryService:

Consumenten in dit patroon bieden een exemplaar van de LibraryOptions klasse, waarbij de gewenste eigenschapswaarden inline worden gedefinieerd:

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

Postconfiguratie

Nadat alle waarden voor configuratieopties zijn gebonden of opgegeven, is de functionaliteit na de configuratie beschikbaar. Als u dezelfde Action<TOptions> parameter weergeeft die eerder is beschreven, kunt u ervoor kiezen om aan te roepen PostConfigure. Na het configureren worden alle .Configure aanroepen uitgevoerd. Er zijn enkele redenen waarom u zou willen overwegen PostConfigure te gebruiken.

  • Uitvoeringsvolgorde: U kunt alle configuratiewaarden die zijn ingesteld in de .Configure aanroepen overschrijven.
  • Validatie: U kunt controleren of de standaardwaarden zijn ingesteld nadat alle andere configuraties zijn toegepast.
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;
    }
}

In de voorgaande code is de AddMyLibraryService:

Consumenten in dit patroon bieden een lambda-expressie (of een delegate die voldoet aan de Action<LibraryOptions> parameter), net zoals ze dat zouden doen bij de Action<TOptions> parameter in een configuratiescenario zonder post:

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

Zie ook