停止在 .NET 中错误使用 IOptions!
Source: Dev.to
(请提供您希望翻译的正文内容,我将为您翻译成简体中文。)
📌 IOptions – 简易版
可以把它想象成: 一张写好后再也不改动的便利贴。
它的作用: 在应用启动时加载设置,仅此而已。
示例
public class NotificationSettings
{
public string SmtpServer { get; set; }
public int Port { get; set; }
public string DefaultSender { get; set; }
}
public class NotificationService
{
private readonly NotificationSettings _settings;
public NotificationService(IOptions<NotificationSettings> options)
{
_settings = options.Value;
}
}
适用场景: 永不改变的设置——例如 SMTP 服务器、数据库 URL、默认发件人邮箱。
注意事项: 如果修改了配置文件,需要重启应用才能看到更改。
📌 IOptionsSnapshot – 面向请求的实现
可以把它想象成: 每次有人找你时,你都检查一下自己的消息。
它的作用: 为每一次进入的 HTTP 请求获取最新的设置。
示例
public class NotificationController : ControllerBase
{
private readonly IOptionsSnapshot<NotificationSettings> _prefs;
public NotificationController(IOptionsSnapshot<NotificationSettings> prefs)
{
_prefs = prefs;
}
[HttpPost("send")]
public IActionResult SendNotification()
{
// 为每个请求获取最新的偏好设置
var enableEmail = _prefs.Value.EnableEmailNotifications;
var enableSms = _prefs.Value.EnableSmsNotifications;
// 根据当前偏好发送通知
// …
return Ok();
}
}
适用场景: 用户偏好、通知开关、A/B 测试——即不同请求可能需要不同设置的情况。
需要注意: 仅在 Web 请求中有效,后台任务中不可用,并且会在每个请求时读取配置。
📌 IOptionsMonitor – 实时更新版
想象它是: 打开实时通知——一有变化你立刻知道。
它的作用: 监视你的配置文件,在你更改时自动更新(无需重启)。
示例
public class NotificationBackgroundService : BackgroundService
{
private readonly IOptionsMonitor<NotificationLimits> _monitor;
public NotificationBackgroundService(IOptionsMonitor<NotificationLimits> monitor)
{
_monitor = monitor;
// Runs automatically when config changes!
_monitor.OnChange(thresholds =>
{
Console.WriteLine("Notification thresholds updated!");
UpdateNotificationRules(thresholds);
});
}
protected override async Task ExecuteAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
// Use current threshold values
var maxPerHour = _monitor.CurrentValue.MaxNotificationsPerHour;
await CheckAndSendNotifications(maxPerHour);
await Task.Delay(TimeSpan.FromMinutes(5), token);
}
}
}
适用场景: 后台作业、计划任务,或任何需要实时更新而不重启的情况。
注意点: 设置稍微复杂,且会占用稍多的内存。
🎯 如何选择?
问自己一个简单的问题:“我的配置在应用运行时会改变吗?”
| 答案 | 推荐选项 |
|---|---|
| 否 | IOptions – 简单且快速 |
| 是 | 是用于 web 请求 还是 后台工作? |
| Web 请求 | IOptionsSnapshot |
| 后台工作 | IOptionsMonitor |
⚠️ 我犯过的错误(让你免于重蹈)
| 错误 | 为什么是问题 |
|---|---|
在后台服务中使用 IOptionsSnapshot | 它仅适用于作用域的 Web 请求 |
将 IOptionsSnapshot.Value 存储在变量中 | 失去“每次请求都新鲜”的优势 |
忘记使用 IOptionsMonitor 监听 OnChange | 你将不知道配置何时更改 |
| 未验证设置 | 当配置错误时,应用可能在随机时间崩溃 |
💡 我早该知道的酷技巧
技巧 #1 – 同类型的多个配置
services.Configure<EmailSettings>("Email", config.GetSection("Email"));
services.Configure<SmsSettings>("SMS", config.GetSection("SMS"));
services.Configure<PushSettings>("Push", config.GetSection("Push"));
// Later in your code:
var emailSettings = _options.Get<EmailSettings>("Email");
var smsSettings = _options.Get<SmsSettings>("SMS");
技巧 #2 – 启动时验证配置
services.AddOptions<NotificationSettings>()
.Validate(settings => !string.IsNullOrEmpty(settings.SmtpServer),
"Hey! You forgot the SMTP Server!")
.Validate(settings => settings.Port > 0 && settings.Port (Configuration.GetSection("NotificationLimits"));
使用(例如,在后台服务中)
public class NotificationWorker : BackgroundService
{
private readonly IOptionsMonitor<NotificationLimits> _limits;
public NotificationWorker(IOptionsMonitor<NotificationLimits> limits)
{
_limits = limits;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var limits = _limits.CurrentValue;
// Use limits.MaxPerUser and limits.TimeWindowHours …
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
{
"NotificationLimits": {
"MaxPerUser": 5,
"EnableEmailNotifications": true,
"EnableSmsNotifications": false
}
}
// When starting your app
builder.Services.Configure<NotificationLimits>(
builder.Configuration.GetSection("NotificationLimits"));
后台服务 – 需要实时更新!
public class NotificationQueueService : BackgroundService
{
private readonly IOptionsMonitor<NotificationLimits> _monitor;
public NotificationQueueService(IOptionsMonitor<NotificationLimits> monitor)
{
_monitor = monitor;
}
protected override async Task ExecuteAsync(CancellationToken token)
{
// Update limits immediately when config changes
_monitor.OnChange(limits =>
{
Console.WriteLine($"Notification limits updated to {limits.MaxPerUser} per user");
UpdateQueueProcessor(limits);
});
while (!token.IsCancellationRequested)
{
await ProcessNotificationQueue(_monitor.CurrentValue);
await Task.Delay(TimeSpan.FromSeconds(10), token);
}
}
}
API 端点 – 需要每次请求都获取最新数据
public class NotificationController : ControllerBase
{
private readonly IOptionsSnapshot<NotificationLimits> _limits;
public NotificationController(IOptionsSnapshot<NotificationLimits> limits)
{
_limits = limits;
}
[HttpPost("send")]
public async Task<IActionResult> SendNotification([FromBody] NotificationRequest request)
{
var maxPerUser = _limits.Value.MaxPerUser;
if (await CheckUserNotificationCount(request.UserId) >= maxPerUser)
return BadRequest("Notification limit reached");
// Each request gets current limits
await SendNotificationAsync(request);
return Ok();
}
}
🎓 记住这个
| 选项 | 何时使用 | 类比 |
|---|---|---|
| IOptions | 写一次,读永久 | 纹身——设定后永久不变 |
| IOptionsSnapshot | 每次请求都有新数据 | 每天早上查看天气 |
| IOptionsMonitor | 配置更改时实时更新 | 社交媒体动态实时更新 |
你以前用过这些吗?
哪一个让你最困惑?在下面留言吧!👇
如果这对你有帮助,请保存以备后用——凌晨 2 点调试时你会感谢自己的!