ASP .NET Core 依赖注入容器中最常用的五大内置服务:使用与扩展(含完整示例)
本文面向 .NET 6+ 的 ASP .NET Core 应用,系统梳理依赖注入(DI)容器中“最常用的五大内置服务”,分别是:
IConfiguration(配置)ILogger<T>(日志)IOptions<T>/IOptionsMonitor<T>/IOptionsSnapshot<T>(选项配置)IHttpClientFactory(HTTP 客户端工厂)IMemoryCache(内存缓存)
1. 预备知识:ASP .NET Core DI 与生命周期
- 注册方式:
- 在
Program.cs中通过builder.Services.AddXxx()注册; - 系统默认会注册一批常用的通用基础设施服务(如配置、日志、选项等)。
- 在
- 生命周期(Lifetime):
- Singleton:应用全局单例,适合无状态且线程安全的服务(如
IConfiguration、ILoggerFactory)。 - Scoped:每个请求一个实例(Web 场景中最常用)。
- Transient:每次注入/解析都创建新实例。
- Singleton:应用全局单例,适合无状态且线程安全的服务(如
- 在控制器、Minimal API、BackgroundService、Middleware 中均可通过构造函数或方法参数注入服务。
2. 五大内置服务总览
IConfiguration- 职责:统一访问配置系统(JSON、环境变量、命令行、用户密钥、远端配置源)。
- 生命周期:Singleton。
ILogger<T>- 职责:结构化日志记录,支持多提供程序(Console、Debug、EventSource、文件等第三方)。
- 生命周期:
ILogger<T>为轻量级工厂产物,按需创建;ILoggerFactory为 Singleton。
IOptions<T>/IOptionsSnapshot<T>/IOptionsMonitor<T>- 职责:强类型配置绑定与访问,支持验证、命名 Options、热更新。
- 生命周期:
IOptions<T>是 Singleton 读取初始值;IOptionsSnapshot<T> Scoped;IOptionsMonitor<T> Singleton+ 变更回调。
IHttpClientFactory- 职责:规范、可复用、可配置、可观测的 HttpClient 创建工厂;支持命名/类型化客户端、处理程序管道。
- 生命周期:工厂 Singleton;HttpClient 本身短生命周期但共享连接池。
IMemoryCache- 职责:进程内缓存,支持过期策略、优先级、逐出回调。
- 生命周期:Singleton。
3. IConfiguration:配置系统
3.1 常见用法
- 自动加载
appsettings.json、appsettings.{Environment}.json、环境变量、命令行参数等。 - 可通过索引器或 GetSection 访问。
- 可与 Options 模式结合进行强类型绑定。
示例(访问配置):
public class HomeController : ControllerBase
{
private readonly IConfiguration _config;
public HomeController(IConfiguration config)
{
_config = config;
}
[HttpGet("/config")]
public IActionResult Get()
{
var appName = _config["App:Name"]; // 直接键访问
var connectionString = _config.GetConnectionString("Default"); // 访问连接串
var sectionValue = _config.GetSection("App:Features:NewUI").Value;
return Ok(new { appName, connectionString, sectionValue });
}
}
在 Program.cs 中,默认已添加配置源,可额外补充:
builder.Configuration
.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables(prefix: "MYAPP_")
.AddInMemoryCollection(new Dictionary<string, string?>
{
["App:InjectedKey"] = "FromInMemory"
});
3.2 与 Options 模式配合(强类型绑定)
public class MySettings
{
public string AppName { get; set; } = "DemoApp";
public bool EnableBeta { get; set; }
}
builder.Services.Configure<MySettings>(builder.Configuration.GetSection("App"));
3.3 如何扩展:自定义配置源
实现 IConfigurationSource 和 IConfigurationProvider 可接入自定义配置源(例如远端 HTTP JSON 配置)。
简化示例(只读、无热更新):
public class SimpleDictionaryConfigurationSource : IConfigurationSource
{
private readonly IDictionary<string, string?> _data;
public SimpleDictionaryConfigurationSource(IDictionary<string, string?> data) => _data = data;
public IConfigurationProvider Build(IConfigurationBuilder builder) => new SimpleDictionaryConfigurationProvider(_data);
}
public class SimpleDictionaryConfigurationProvider : ConfigurationProvider
{
public SimpleDictionaryConfigurationProvider(IDictionary<string, string?> data)
{
Data = new Dictionary<string, string?>(data, StringComparer.OrdinalIgnoreCase);
}
}
// 注册
builder.Configuration.Add(new SimpleDictionaryConfigurationSource(new Dictionary<string, string?>
{
["Feature:AutoSave"] = "true",
["Feature:MaxItems"] = "100"
}));
要支持热更新,可结合 IChangeToken,自行触发 OnReload()。
4. ILogger<T>:结构化日志
4.1 常见用法
- 注入
ILogger<T>,使用LogInformation/LogWarning/LogError等方法。 - 结构化日志:使用命名占位符
{Name}提供模板化字段,方便查询与分析。 - 日志作用域:使用 BeginScope 增加上下文。
public class OrderService
{
private readonly ILogger<OrderService> _logger;
public OrderService(ILogger<OrderService> logger) => _logger = logger;
public void PlaceOrder(string userId, decimal amount)
{
using (_logger.BeginScope(new Dictionary<string, object> { ["UserId"] = userId }))
{
_logger.LogInformation("Placing order with amount {Amount}", amount);
try
{
// 业务逻辑
}
catch (Exception ex)
{
_logger.LogError(ex, "PlaceOrder failed for user {UserId}", userId);
throw;
}
}
}
}
在 appsettings.json 配置日志级别与输出:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information",
"YourNamespace": "Debug"
}
}
}
4.2 如何扩展:自定义日志提供程序
实现 ILoggerProvider 与 ILogger,可将日志写入文件、数据库等。
简化文件日志示例:
public class FileLoggerProvider : ILoggerProvider
{
private readonly string _filePath;
public FileLoggerProvider(string filePath) => _filePath = filePath;
public ILogger CreateLogger(string categoryName) => new FileLogger(_filePath, categoryName);
public void Dispose() { }
}
public class FileLogger : ILogger
{
private readonly string _filePath;
private readonly string _category;
public FileLogger(string filePath, string category) { _filePath = filePath; _category = category; }
public IDisposable? BeginScope<TState>(TState state) => default;
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel)) return;
var line = $"{DateTimeOffset.Now:o} [{logLevel}] {_category} {formatter(state, exception)}{(exception is null ? "" : Environment.NewLine + exception)}";
File.AppendAllText(_filePath, line + Environment.NewLine);
}
}
// 注册扩展
public static class FileLoggerExtensions
{
public static ILoggingBuilder AddSimpleFile(this ILoggingBuilder builder, string filePath)
{
builder.AddProvider(new FileLoggerProvider(filePath));
return builder;
}
}
// Program.cs
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddSimpleFile(Path.Combine(AppContext.BaseDirectory, "app.log"));
5. Options 模式:IOptions<T>/IOptionsSnapshot<T>/IOptionsMonitor<T>
5.1 三者区别
IOptions<T>:单例快照,适合不变或只在启动时读取一次的配置。IOptionsSnapshot<T>:每次请求生成新实例(Scoped),适合 Web 请求场景下的“每请求最新”。IOptionsMonitor<T>:单例 + 变更通知,适合需要订阅配置变更并即时响应的服务。
5.2 基础使用与命名 Options
public class RedisOptions
{
public string ConnectionString { get; set; } = default!;
public int Database { get; set; }
}
// 默认(未命名)
builder.Services.Configure<RedisOptions>(builder.Configuration.GetSection("Redis:Primary"));
// 命名 Options
builder.Services.Configure<RedisOptions>("Secondary", builder.Configuration.GetSection("Redis:Secondary"));
public class CacheService
{
private readonly RedisOptions _defaultOptions;
private readonly IOptionsMonitor<RedisOptions> _monitor;
public CacheService(IOptions<RedisOptions> options, IOptionsMonitor<RedisOptions> monitor)
{
_defaultOptions = options.Value;
_monitor = monitor;
_monitor.OnChange((opt, name) =>
{
Console.WriteLine($"Redis options changed. Name={name}, Conn={opt.ConnectionString}");
});
}
public RedisOptions GetSecondary() => _monitor.Get("Secondary");
}
5.3 验证与后置配置
- DataAnnotations 验证:使用 ValidateDataAnnotations。
- 自定义验证:实现
IValidateOptions<T>。 - 后置配置:
IPostConfigureOptions<T>用于在绑定后追加逻辑。
public class MySettings
{
[Required, MinLength(3)]
public string AppName { get; set; } = default!;
[Range(1, 100)]
public int MaxItems { get; set; } = 10;
}
builder.Services
.AddOptions<MySettings>()
.Bind(builder.Configuration.GetSection("App"))
.ValidateDataAnnotations()
.Validate(o => o.MaxItems >= 5, "MaxItems must be >= 5")
.ValidateOnStart(); // 启动时即验证,避免运行期才暴雷
// 自定义验证
public class MySettingsValidator : IValidateOptions<MySettings>
{
public ValidateOptionsResult Validate(string? name, MySettings options)
{
if (options.AppName?.Contains("bad", StringComparison.OrdinalIgnoreCase) == true)
return ValidateOptionsResult.Fail("AppName cannot contain 'bad'.");
return ValidateOptionsResult.Success;
}
}
builder.Services.AddSingleton<IValidateOptions<MySettings>, MySettingsValidator>();
6. IHttpClientFactory:标准化 HttpClient
6.1 基础用法
- 使用
AddHttpClient注册; - 支持命名客户端与类型化客户端;
- 支持 DelegatingHandler 处理管道(重试、熔断、鉴权、指标收集等);
- 内置连接池,避免 socket 耗尽。
// 命名客户端
builder.Services.AddHttpClient("github", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
client.Timeout = TimeSpan.FromSeconds(10);
});
// 类型化客户端
public interface IWeatherClient
{
Task<string> GetAsync(string city, CancellationToken ct = default);
}
public class WeatherClient : IWeatherClient
{
private readonly HttpClient _http;
public WeatherClient(HttpClient http) => _http = http;
public Task<string> GetAsync(string city, CancellationToken ct = default)
=> _http.GetStringAsync($"/weather?city={Uri.EscapeDataString(city)}", ct);
}
builder.Services.AddHttpClient<IWeatherClient, WeatherClient>(client =>
{
client.BaseAddress = new Uri("https://weather.example.com");
});
使用 IHttpClientFactory 创建命名客户端:
public class GitHubService
{
private readonly IHttpClientFactory _factory;
public GitHubService(IHttpClientFactory factory) => _factory = factory;
public async Task<string> GetUserAsync(string user)
{
var client = _factory.CreateClient("github");
return await client.GetStringAsync($"/users/{user}");
}
}
6.2 自定义 DelegatingHandler(扩展)
public class AddHeaderHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.TryAddWithoutValidation("X-Correlation-Id", Guid.NewGuid().ToString("N"));
return base.SendAsync(request, cancellationToken);
}
}
builder.Services.AddTransient<AddHeaderHandler>();
builder.Services.AddHttpClient("github")
.AddHttpMessageHandler<AddHeaderHandler>() // 加入管道
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
});
提示:
- 若需重试/超时/熔断,可引入
Microsoft.Extensions.Http.Resilience(.NET 8+)或 Polly 扩展包进行策略化处理。 - 观察和日志:
HttpClientFactory生成的客户端默认可被日志系统观测。
7. IMemoryCache:进程内缓存
7.1 基础用法
Get/Set,或使用GetOrCreate/GetOrCreateAsync。- 过期策略:AbsoluteExpiration、SlidingExpiration。
- 优先级:CacheItemPriority。
- 逐出回调:PostEvictionCallbacks。
public class ProductCache
{
private readonly IMemoryCache _cache;
private readonly ILogger<ProductCache> _logger;
public ProductCache(IMemoryCache cache, ILogger<ProductCache> logger)
{
_cache = cache; _logger = logger;
}
public async Task<Product> GetProductAsync(int id, Func<Task<Product>> factory)
{
return await _cache.GetOrCreateAsync($"product:{id}", async entry =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
entry.SetSlidingExpiration(TimeSpan.FromMinutes(1));
entry.SetPriority(CacheItemPriority.Normal);
entry.RegisterPostEvictionCallback((key, value, reason, state) =>
{
_logger.LogInformation("Cache evicted. Key={Key}, Reason={Reason}", key, reason);
});
return await factory();
})!;
}
}
7.2 与变更令牌(IChangeToken)联动失效
可通过 OptionsMonitor 的 OnChange 或自定义 IChangeToken,调用 cache.Remove(key) 主动失效。
public class FeatureFlagService
{
private readonly IMemoryCache _cache;
private readonly IOptionsMonitor<MySettings> _settings;
public FeatureFlagService(IMemoryCache cache, IOptionsMonitor<MySettings> settings)
{
_cache = cache; _settings = settings;
_settings.OnChange(_ => _cache.Remove("feature:beta")); // 配置变更时清理缓存
}
public bool IsBetaEnabled()
{
return _cache.GetOrCreate("feature:beta", entry =>
{
entry.SetSlidingExpiration(TimeSpan.FromSeconds(30));
return _settings.CurrentValue.EnableBeta;
});
}
}
7.3 扩展方式
- 装饰器:实现一个包装
IMemoryCache的自定义类用于打点、指标、Tracing。 - 自定义实现:实现
IMemoryCache接口(较少必要,注意线程安全)。 - 扩展方法:封装常见模式(如基于 Key 的统一过期策略)。
8. 完整示例:Program.cs 与多个服务
下列示例展示一个可运行的最小化 API 应用,演示五大内置服务的典型用法与扩展点。
目录结构建议:
Program.csServices/OrderService.csGitHubService.csProductCache.csHandlers/AddHeaderHandler.csLogging/FileLoggerProvider.cs
Options/MySettings.csRedisOptions.csMySettingsValidator.cs
示例代码(可粘贴到 Program.cs 验证,必要时创建相应类文件):
using System.ComponentModel.DataAnnotations;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
// ---------------* Options classes ----------------
public class MySettings
{
[Required, MinLength(3)]
public string AppName { get; set; } = "DemoApp";
[Range(1, 100)]
public int MaxItems { get; set; } = 10;
public bool EnableBeta { get; set; }
}
public class RedisOptions
{
public string ConnectionString { get; set; } = "localhost";
public int Database { get; set; } = 0;
}
// ---------------* Custom validator ----------------
public class MySettingsValidator : Microsoft.Extensions.Options.IValidateOptions<MySettings>
{
public Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, MySettings options)
{
if (options.AppName.Contains("bad", StringComparison.OrdinalIgnoreCase))
return Microsoft.Extensions.Options.ValidateOptionsResult.Fail("AppName cannot contain 'bad'.");
return Microsoft.Extensions.Options.ValidateOptionsResult.Success;
}
}
// ---------------* DelegatingHandler ----------------
public class AddHeaderHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.TryAddWithoutValidation("X-Correlation-Id", Guid.NewGuid().ToString("N"));
return base.SendAsync(request, cancellationToken);
}
}
// ---------------* File logger (simple) ----------------
public class FileLoggerProvider : ILoggerProvider
{
private readonly string _filePath;
public FileLoggerProvider(string filePath) => _filePath = filePath;
public ILogger CreateLogger(string categoryName) => new FileLogger(_filePath, categoryName);
public void Dispose() { }
}
public class FileLogger : ILogger
{
private readonly string _filePath;
private readonly string _category;
public FileLogger(string filePath, string category) { _filePath = filePath; _category = category; }
public IDisposable? BeginScope<TState>(TState state) => default;
public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel)) return;
var line = $"{DateTimeOffset.Now:o} [{logLevel}] {_category} {formatter(state, exception)}{(exception is null ? "" : Environment.NewLine + exception)}";
File.AppendAllText(_filePath, line + Environment.NewLine);
}
}
public static class FileLoggerExtensions
{
public static ILoggingBuilder AddSimpleFile(this ILoggingBuilder builder, string filePath)
{
builder.AddProvider(new FileLoggerProvider(filePath));
return builder;
}
}
// ---------------* Services ----------------
public class OrderService
{
private readonly ILogger<OrderService> _logger;
private readonly IOptionsSnapshot<MySettings> _settings;
public OrderService(ILogger<OrderService> logger, IOptionsSnapshot<MySettings> settings)
{
_logger = logger; _settings = settings;
}
public string PlaceOrder(decimal amount)
{
using (_logger.BeginScope(new Dictionary<string, object> { ["OrderId"] = Guid.NewGuid() }))
{
_logger.LogInformation("Placing order with amount {Amount}", amount);
return $"OK:MaxItems={_settings.Value.MaxItems}";
}
}
}
public interface IWeatherClient
{
Task<string> GetAsync(string city, CancellationToken ct = default);
}
public class WeatherClient : IWeatherClient
{
private readonly HttpClient _http;
public WeatherClient(HttpClient http) => _http = http;
public Task<string> GetAsync(string city, CancellationToken ct = default)
=> _http.GetStringAsync($"/weather?city={Uri.EscapeDataString(city)}", ct);
}
public class GitHubService
{
private readonly IHttpClientFactory _factory;
public GitHubService(IHttpClientFactory factory) => _factory = factory;
public async Task<string> GetUserAsync(string user, CancellationToken ct = default)
{
var client = _factory.CreateClient("github");
return await client.GetStringAsync($"/users/{user}", ct);
}
}
public record Product(int Id, string Name);
public class ProductCache
{
private readonly IMemoryCache _cache;
private readonly ILogger<ProductCache> _logger;
public ProductCache(IMemoryCache cache, ILogger<ProductCache> logger)
{
_cache = cache; _logger = logger;
}
public async Task<Product> GetProductAsync(int id, Func<Task<Product>> factory)
{
return await _cache.GetOrCreateAsync($"product:{id}", async entry =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));
entry.SetSlidingExpiration(TimeSpan.FromMinutes(1));
entry.RegisterPostEvictionCallback((key, value, reason, state) =>
{
_logger.LogInformation("Cache evicted. Key={Key}, Reason={Reason}", key, reason);
});
return await factory();
})!;
}
}
// ---------------* Program ----------------
var builder = WebApplication.CreateBuilder(args);
// 配置源扩展(演示)
builder.Configuration
.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true)
.AddInMemoryCollection(new Dictionary<string, string?>
{
["App:AppName"] = "MyShop",
["Redis:Primary:ConnectionString"] = "localhost:6379",
["Redis:Primary:Database"] = "0"
});
// 日志:控制台 + 简易文件
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddSimpleFile(Path.Combine(AppContext.BaseDirectory, "app.log"));
// Options:强类型绑定 + 验证
builder.Services
.AddOptions<MySettings>()
.Bind(builder.Configuration.GetSection("App"))
.ValidateDataAnnotations()
.Validate(o => o.MaxItems >= 5, "MaxItems must be >= 5")
.ValidateOnStart();
builder.Services.AddSingleton<Microsoft.Extensions.Options.IValidateOptions<MySettings>, MySettingsValidator>();
builder.Services.Configure<RedisOptions>(builder.Configuration.GetSection("Redis:Primary"));
builder.Services.Configure<RedisOptions>("Secondary", builder.Configuration.GetSection("Redis:Secondary"));
// HttpClientFactory:命名客户端与类型化客户端
builder.Services.AddTransient<AddHeaderHandler>();
builder.Services.AddHttpClient("github", client =>
{
client.BaseAddress = new Uri("https://api.github.com/");
client.DefaultRequestHeaders.UserAgent.ParseAdd("MyApp/1.0");
client.Timeout = TimeSpan.FromSeconds(10);
})
.AddHttpMessageHandler<AddHeaderHandler>()
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
});
builder.Services.AddHttpClient<IWeatherClient, WeatherClient>(client =>
{
client.BaseAddress = new Uri("https://weather.example.com");
});
// 内存缓存
builder.Services.AddMemoryCache();
// 业务服务
builder.Services.AddScoped<OrderService>();
builder.Services.AddSingleton<ProductCache>();
builder.Services.AddSingleton<GitHubService>();
var app = builder.Build();
app.MapGet("/", (IConfiguration cfg, IOptions<MySettings> opts) =>
{
return Results.Ok(new
{
Hello = "World",
AppName = cfg["App:AppName"],
Options_AppName = opts.Value.AppName
});
});
app.MapPost("/order", (OrderService svc, [FromBody] decimal amount) =>
{
var result = svc.PlaceOrder(amount);
return Results.Ok(result);
});
app.MapGet("/github/{user}", async (GitHubService svc, string user, CancellationToken ct) =>
{
var json = await svc.GetUserAsync(user, ct);
return Results.Text(json, "application/json");
});
app.MapGet("/product/{id:int}", async (int id, ProductCache cache) =>
{
var p = await cache.GetProductAsync(id, async () =>
{
await Task.Delay(100); // 模拟 IO
return new Product(id, $"P-{id}");
});
return Results.Ok(p);
});
app.Run();
appsettings.json示例(关键片段):
{
"App": {
"AppName": "MyShop",
"MaxItems": 20,
"EnableBeta": true
},
"Redis": {
"Primary": {
"ConnectionString": "localhost:6379",
"Database": 0
},
"Secondary": {
"ConnectionString": "localhost:6380",
"Database": 1
}
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
9. 最佳实践与常见坑
- 生命周期选择
- 配置、日志、HttpClientFactory、内存缓存多为 Singleton。
- 业务服务大多用 Scoped(Web 请求内共享)。
- HttpClient 请使用 IHttpClientFactory
- 避免自己
new HttpClient的长期单例或频繁创建导致 Socket 耗尽。 - 使用处理程序管道统一注入鉴权、追踪 ID、重试等横切逻辑。
- 避免自己
- 配置验证尽量在启动时进行
- 使用 ValidateOnStart,第一时间发现错误(容器启动即失败)。
- 日志要结构化
- 使用
{FieldName}模板,利于后续检索与度量。
- 使用
- 缓存键规范与过期策略
- 使用统一的 key 前缀与过期策略,避免内存膨胀与脏数据。
- 尽量缓存不可变数据或快速可重建数据,必要时配合 IDistributedCache 做分布式缓存。
- OptionsSnapshot 与后台线程
- IOptionsSnapshot 是 Scoped,仅适用于请求上下文;后台服务请使用 IOptionsMonitor。
- 配置热更新与缓存失效联动
- 结合
IOptionsMonitor.OnChange清理相关缓存或重置内部状态。
- 结合
- 自定义提供程序注意线程安全和性能
- 日志、配置源、缓存等自定义实现需充分考虑并发与 IO 锁争用。
10. 延伸阅读(官方文档)
- 依赖注入: https://learn.microsoft.com/aspnet/core/fundamentals/dependency-injection
- 配置: https://learn.microsoft.com/aspnet/core/fundamentals/configuration
- 日志: https://learn.microsoft.com/aspnet/core/fundamentals/logging
- Options 模式: https://learn.microsoft.com/aspnet/core/fundamentals/configuration/options
- IHttpClientFactory: https://learn.microsoft.com/aspnet/core/fundamentals/http-requests
- 内存缓存: https://learn.microsoft.com/aspnet/core/performance/caching/memory
通过以上内容,可以在 ASP .NET Core 中熟练地使用并扩展五大常用内置服务,形成“配置-日志-选项-HTTP-缓存”的稳定基础设施闭环,从而为复杂业务打下坚实的可维护与可观测基石。