Pokyny k vzoru možností pro autory knihoven .NET
Díky injektáži závislostí může registrace služeb a jejich odpovídající konfigurace využít vzor možností. Vzor možností umožňuje uživatelům vaší knihovny (a vašim službám) vyžadovat instance rozhraní možností, kde TOptions
je třída možností. Využívání možností konfigurace prostřednictvím objektů silného typu pomáhá zajistit konzistentní reprezentaci hodnot, umožňuje ověřování pomocí datových poznámek a odstraňuje zátěž ruční analýzy řetězcových hodnot. Uživatelé vaší knihovny mohou používat mnoho poskytovatelů konfigurace. S těmito poskytovateli můžou uživatelé konfigurovat vaši knihovnu mnoha způsoby.
Jako autor knihovny .NET se dozvíte obecné pokyny, jak správně vystavit vzor možností uživatelům knihovny. Existují různé způsoby, jak dosáhnout stejné věci, a několik aspektů, které je potřeba vzít v úvahu.
Zásady vytváření názvů
Podle konvence jsou metody rozšíření zodpovědné za registraci služeb pojmenovány Add{Service}
, kde {Service}
je smysluplný a popisný název. Add{Service}
Metody rozšíření jsou běžné v ASP.NET Core a .NET.
✔️ ZVAŽTE názvy, které nejednoznačností vaší služby od jiných nabídek.
❌ NEPOUŽÍVEJTE názvy, které jsou již součástí ekosystému .NET z oficiálních balíčků Microsoftu.
✔️ ZVAŽTE pojmenování statických tříd, které zpřístupňují rozšiřující metody jako {Type}Extensions
, kde {Type}
je typ, který rozšiřujete.
Pokyny k oboru názvů
Balíčky Microsoftu využívají Microsoft.Extensions.DependencyInjection
obor názvů ke sjednocení registrace různých nabídek služeb.
✔️ ZVAŽTE obor názvů, který jasně identifikuje vaši nabídku balíčků.
❌ NEPOUŽÍVEJTE Microsoft.Extensions.DependencyInjection
obor názvů pro jiné než oficiální balíčky Společnosti Microsoft.
Bez parametrů
Pokud vaše služba může pracovat s minimální nebo žádnou explicitní konfigurací, zvažte metodu rozšíření bez parametrů.
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;
}
}
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- Volání OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) s parametrem typu
LibraryOptions
- Zřetězí volání Configure, které určuje výchozí hodnoty možností.
IConfiguration
Parametr
Když vytvoříte knihovnu, která uživatelům zpřístupňuje mnoho možností, můžete zvážit vyžadování metody rozšíření parametru IConfiguration
. Očekávaná IConfiguration
instance by měla být vymezena na pojmenovanou část konfigurace pomocí IConfiguration.GetSection funkce.
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
Metoda Configure<TOptions>(IServiceCollection, IConfiguration) je součástí Microsoft.Extensions.Options.ConfigurationExtensions
balíčku NuGet.
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- IConfiguration Definuje parametr.
namedConfigurationSection
- Volání Configure<TOptions>(IServiceCollection, IConfiguration) , která předávají parametr
LibraryOptions
obecného typu anamedConfigurationSection
instanci ke konfiguraci
Příjemci v tomto vzoru poskytují vymezenou IConfiguration
instanci pojmenovaného oddílu:
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();
Volání .AddMyLibraryService
se provede u IServiceCollection typu.
Jako autor knihovny je zadání výchozích hodnot na vás.
Poznámka:
Konfiguraci je možné svázat s instancí možností. Existuje však riziko kolizí názvů , což způsobí chyby. Navíc když tímto způsobem ručně vytvoříte vazbu, omezíte spotřebu vzorů možností na čtení jednou. Změny nastavení nebudou znovu vázány, protože uživatelé nebudou moci používat rozhraní IOptionsMonitor .
services.AddOptions<LibraryOptions>()
.Configure<IConfiguration>(
(options, configuration) =>
configuration.GetSection("LibraryOptions").Bind(options));
Místo toho byste měli použít metodu BindConfiguration rozšíření. Tato metoda rozšíření sváže konfiguraci s instancí možností a také zaregistruje zdroj tokenu změn pro oddíl konfigurace. To umožňuje uživatelům používat rozhraní IOptionsMonitor .
Parametr cesty oddílu konfigurace
Příjemci vaší knihovny mohou chtít zadat cestu ke konfiguračnímu oddílu pro vytvoření vazby souvisejícího TOptions
typu. V tomto scénáři definujete string
parametr v metodě rozšíření.
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;
}
}
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- Definuje
string
parametr.configSectionPath
- Volání:
- AddOptions parametrem obecného typu
SupportOptions
- BindConfiguration s daným
configSectionPath
parametrem - ValidateDataAnnotations povolení ověřování datových poznámek
- ValidateOnStart vynucení ověřování při spuštění místo v modulu runtime
- AddOptions parametrem obecného typu
V dalším příkladu se k povolení ověření datových poznámek použije balíček NuGet Microsoft.Extensions.Options.DataAnnotations . Třída SupportOptions
je definována takto:
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; }
}
Představte si, že se používá následující soubor JSON appsettings.json :
{
"Support": {
"Url": "https://support.example.com",
"Email": "help@support.example.com",
"PhoneNumber": "+1(888)-SUPPORT"
}
}
Action<TOptions>
Parametr
Spotřebitelé vaší knihovny mohou mít zájem o poskytnutí výrazu lambda, který poskytuje instanci vaší třídy možností. V tomto scénáři definujete Action<LibraryOptions>
parametr v metodě rozšíření.
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;
}
}
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- Action<T> Definuje parametr
configureOptions
, kdeT
jeLibraryOptions
- Volání Configure s danou
configureOptions
akcí
Příjemci v tomto vzoru poskytují výraz lambda (nebo delegát, který tento Action<LibraryOptions>
parametr splňuje):
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();
Parametr instance možnosti
Příjemci vaší knihovny můžou raději poskytnout inlinovanou instanci možností. V tomto scénáři zveřejníte rozšiřující metodu, která přebírá instanci objektu 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;
}
}
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- Volání OptionsServiceCollectionExtensions.AddOptions<TOptions>(IServiceCollection) s parametrem typu
LibraryOptions
- Zřetězí volání Configure, které určuje výchozí hodnoty možností, které lze přepsat z dané
userOptions
instance.
Příjemci v tomto vzoru poskytují instanci třídy, která definuje hodnoty požadovaných LibraryOptions
vlastností v textu:
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();
Po konfiguraci
Po vázání nebo zadání všech hodnot možností konfigurace je k dispozici funkce po konfiguraci. Zveřejnění stejného Action<TOptions>
parametru , který je podrobně popsán dříve, můžete zvolit volání PostConfigure. Po dokončení konfigurace se spustí po všech .Configure
voláních. Existuje několik důvodů, proč byste měli zvážit použití PostConfigure
:
- Pořadí provádění: Můžete přepsat všechny hodnoty konfigurace, které byly nastaveny ve
.Configure
voláních. - Ověření: Po použití všech ostatních konfigurací můžete ověřit, jestli jsou nastavené výchozí hodnoty.
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;
}
}
V předchozím kódu:AddMyLibraryService
- Rozšíří instanci IServiceCollection
- Action<T> Definuje parametr
configureOptions
, kdeT
jeLibraryOptions
- Volání PostConfigure s danou
configureOptions
akcí
Příjemci v tomto vzoru poskytují výraz lambda (nebo delegát, který tento parametr splňujeAction<LibraryOptions>
), stejně jako u parametru Action<TOptions>
ve scénáři konfigurace, který není po dokončení konfigurace:
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();