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.
Naamconventies
Standaard worden extensiemethoden die verantwoordelijk zijn voor het registreren van services benoemd Add{Service}
, waar {Service}
een beschrijvende en beschrijvende naam is. Add{Service}
extensiemethoden zijn gebruikelijk in ASP.NET Core en .NET.
✔️ HOUD REKENING met namen die uw service niet duidelijk maken 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 naamruimten
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 het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Aanroepen OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) met de typeparameter van
LibraryOptions
- Koppelt een aanroep naar Configure, waarmee de standaardoptiewaarden worden opgegeven
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;
}
}
Tip
De Configure<TOptions>(IServiceCollection, IConfiguration) methode maakt deel uit van het Microsoft.Extensions.Options.ConfigurationExtensions
NuGet-pakket.
In de voorgaande code is het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Definieert een IConfiguration parameter
namedConfigurationSection
- Aanroepen Configure<TOptions>(IServiceCollection, IConfiguration) die de algemene typeparameter doorgeven
LibraryOptions
en hetnamedConfigurationSection
exemplaar dat moet worden geconfigureerd
Consumenten in dit patroon bieden het bereikexemplaren van de benoemde IConfiguration
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 wordt .AddMyLibraryService
uitgevoerd op het IServiceCollection type.
Als auteur van de bibliotheek kunt u standaardwaarden opgeven.
Notitie
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 één keer. 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.
Parameter configuratiesectiepad
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 het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Definieert een
string
parameterconfigSectionPath
- Oproepen:
- AddOptions met de algemene typeparameter van
SupportOptions
- BindConfiguration met de opgegeven
configSectionPath
parameter - ValidateDataAnnotations gegevensannotatievalidatie inschakelen
- ValidateOnStart validatie afdwingen bij starten in plaats van in runtime
- AddOptions met de algemene typeparameter van
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 het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Definieert een parameter
configureOptions
waarT
zich bevindt Action<T>LibraryOptions
- Aanroepen die de
configureOptions
actie hebben gekregen Configure
Consumenten in dit patroon bieden een lambda-expressie (of een gemachtigde die voldoet aan 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 het exemplaar van opties
Consumenten van uw bibliotheek geven mogelijk de voorkeur aan een exemplaar van inlineopties. 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 het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Aanroepen OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) met de typeparameter van
LibraryOptions
- Koppelt een aanroep naar Configure, waarmee standaardoptiewaarden worden opgegeven die kunnen worden overschreven vanuit het opgegeven
userOptions
exemplaar
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 het gebruik PostConfigure
van:
- 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 het AddMyLibraryService
volgende:
- Breidt een exemplaar van IServiceCollection
- Definieert een parameter
configureOptions
waarT
zich bevindt Action<T>LibraryOptions
- Aanroepen die de
configureOptions
actie hebben gekregen PostConfigure
Consumenten in dit patroon bieden een lambda-expressie (of een gemachtigde die voldoet aan de Action<LibraryOptions>
parameter), net zoals bij de Action<TOptions>
parameter in een niet-postconfiguratiescenario:
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();