DEV Community

carbon fin
carbon fin

Posted on

Stop Using IOptions Wrong in .NET!

Ever got confused about which one to use in your .NET app? You're not alone! Let me break it down in simple words. 🧡


πŸ“Œ IOptions - The Simple One

Think of it as: A sticky note you write once and never change

What it does: Loads your settings when the app starts, and that's it!

Example:

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Use it for: Stuff that never changes - like your SMTP server, database URL, or default sender email
The catch: You change the config file? Too bad, restart the app to see changes!


πŸ“Œ IOptionsSnapshot - The Request-Friendly One

Think of it as: Checking your messages every time someone asks you

What it does: Gets fresh settings for every HTTP request that comes in

Example:

public class NotificationController : ControllerBase
{
    private readonly IOptionsSnapshot<NotificationPreferences> _prefs;

    public NotificationController(IOptionsSnapshot<NotificationPreferences> prefs)
    {
        _prefs = prefs;
    }

    [HttpPost("send")]
    public IActionResult SendNotification()
    {
        // Gets fresh preferences for each request
        var enableEmail = _prefs.Value.EnableEmailNotifications;
        var enableSms = _prefs.Value.EnableSmsNotifications;

        // Send based on current preferences
    }
}
Enter fullscreen mode Exit fullscreen mode

Use it for: User preferences, notification toggles, A/B testing when different requests might need different settings
The catch: Only works in web requests, not in background jobs. Also, reads config every time!


πŸ“Œ IOptionsMonitor - The Live Updates One

Think of it as: Having live notifications turned ON - you know immediately when something changes

What it does: Watches your config file and updates automatically when you change it (no restart needed!)

Example:

public class NotificationBackgroundService : BackgroundService
{
    private readonly IOptionsMonitor<NotificationThresholds> _monitor;

    public NotificationBackgroundService(IOptionsMonitor<NotificationThresholds> monitor)
    {
        _monitor = monitor;

        // This 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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Use it for: Background jobs, scheduled tasks, when you need live updates without restarting
The catch: A bit more complex to set up, uses slightly more memory


🎯 How Do I Choose?

Ask yourself one simple question: "Will my config change while the app is running?"

NO β†’ Use IOptions<T> (simple and fast!)

YES β†’ Ask another question: "Is this for web requests or background work?"

  • Web requests β†’ Use IOptionsSnapshot<T>
  • Background work β†’ Use IOptionsMonitor<T>

That's it!


⚠️ Mistakes I've Made (so you don't have to):

  1. Using IOptionsSnapshot in a background service β†’ Won't work! It's only for web requests
  2. Saving IOptionsSnapshot.Value in a variable β†’ You'll lose the "fresh every request" benefit
  3. Forgetting to listen to OnChange with IOptionsMonitor β†’ You won't know when config changes!
  4. Not validating settings β†’ Your app will crash at random times when config is wrong

πŸ’‘ Cool Tricks I Wish I Knew Earlier:

Trick #1: Multiple configs of the same type
Got different notification settings for different channels? No problem!

services.Configure<NotificationSettings>("Email", config.GetSection("Email"));
services.Configure<NotificationSettings>("SMS", config.GetSection("SMS"));
services.Configure<NotificationSettings>("Push", config.GetSection("Push"));

// Later in your code:
var emailSettings = _options.Get("Email");
var smsSettings = _options.Get("SMS");
Enter fullscreen mode Exit fullscreen mode

Trick #2: Make sure your config is correct from the start

services.AddOptions<NotificationSettings>()
    .Validate(settings => !string.IsNullOrEmpty(settings.SmtpServer),
              "Hey! You forgot the SMTP Server!")
    .Validate(settings => settings.Port > 0 && settings.Port < 65535,
              "Invalid SMTP port number!")
    .ValidateOnStart(); // Checks this when app starts, not when it crashes!
Enter fullscreen mode Exit fullscreen mode

πŸ“Š Simple Comparison:

Simple comparison


πŸš€ Real Example from My Projects:

Imagine you have a notification system that needs to respect user preferences:

// appsettings.json - your config file
{
  "NotificationLimits": {
    "MaxPerUser": 50,
    "TimeWindowHours": 24,
    "EnableEmailNotifications": true,
    "EnableSmsNotifications": false
  }
}

// When starting your app
builder.Services.Configure<NotificationLimits>(
    builder.Configuration.GetSection("NotificationLimits"));

// Background service - needs live updates!
public class NotificationQueueService : BackgroundService
{
    private readonly IOptionsMonitor<NotificationLimits> _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 endpoint - needs fresh data per request
public class NotificationController : ControllerBase
{
    private readonly IOptionsSnapshot<NotificationLimits> _limits;

    [HttpPost("send")]
    public 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();
    }
}
Enter fullscreen mode Exit fullscreen mode

See? Same settings, different use cases, different Options!


πŸŽ“ Remember This:

  • IOptions = Write once, read forever (like a tattoo πŸ˜„)
  • IOptionsSnapshot = Fresh data every request (like checking the weather)
  • IOptionsMonitor = Live updates (like your social media feed)

Have you used these before? Which one confused you most? Drop a comment! πŸ‘‡

And hey, if this helped you, save it for later - you'll thank me when you're debugging at 2 AM!

Top comments (0)