0Pricing
C# Academy · Lezione

Convalida delle opzioni e opzioni denominate

Convalidi le opzioni all'avvio con DataAnnotations o FluentValidation e usi opzioni denominate per più istanze.

Convalida delle opzioni e opzioni denominate è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Perché convalidare le opzioni?

Una configurazione mancante o non valida causa errori a runtime difficili da rintracciare. Convalidare le opzioni all'avvio trasforma i problemi di configurazione silenziosi in eccezioni immediate e descrittive, prima che venga gestita qualsiasi richiesta.

ValidateOnStart

ValidateOnStart() attiva immediatamente la convalida all'avvio dell'applicazione, anziché rimandarla al primo utilizzo. In questo modo una distribuzione configurata in modo errato fallisce subito, invece di farlo dopo ore.

builder.Services
    .AddOptions<DatabaseOptions>()
    .BindConfiguration("Database")
    .ValidateDataAnnotations()
    .ValidateOnStart(); // throw OptionsValidationException on startup

// Without ValidateOnStart:
// - Validation only runs on first IOptions<T>.Value access
// - A rarely-used service could run for hours before failing

// With ValidateOnStart:
// - App fails at Host.Run() if config is invalid
// - Health checks and probes never report healthy for bad config

Convalida con DataAnnotations

Utilizzi gli attributi standard di System.ComponentModel.DataAnnotations nella classe delle opzioni. Vengono valutati automaticamente da ValidateDataAnnotations().

using System.ComponentModel.DataAnnotations;

public class EmailOptions
{
    [Required(ErrorMessage = "SMTP host is required")]
    [MinLength(3)]
    public string SmtpHost { get; set; } = string.Empty;

    [Range(1, 65535, ErrorMessage = "Port must be 1-65535")]
    public int SmtpPort { get; set; } = 587;

    [Required]
    [EmailAddress(ErrorMessage = "Invalid sender address")]
    public string SenderAddress { get; set; } = string.Empty;

    [Range(1, 30)]
    public int TimeoutSeconds { get; set; } = 10;
}

builder.Services
    .AddOptions<EmailOptions>()
    .BindConfiguration("Email")
    .ValidateDataAnnotations()
    .ValidateOnStart();

Delegato di convalida personalizzato

L'overload Validate(Func<T, bool>, string) aggiunge una lambda per le regole che non possono essere espresse con gli attributi, come i vincoli tra proprietà.

builder.Services
    .AddOptions<ConnectionPoolOptions>()
    .BindConfiguration("ConnectionPool")
    .ValidateDataAnnotations()
    .Validate(
        opts => opts.MaxSize >= opts.MinSize,
        "MaxSize must be greater than or equal to MinSize")
    .Validate(
        opts => opts.ConnectionTimeoutMs > 0,
        "ConnectionTimeoutMs must be positive")
    .ValidateOnStart();

public class ConnectionPoolOptions
{
    [Range(1, 100)] public int MinSize { get; set; } = 2;
    [Range(1, 500)] public int MaxSize { get; set; } = 20;
    public int ConnectionTimeoutMs { get; set; } = 5000;
}

IValidateOptions per regole complesse

Per una logica complessa con più messaggi di errore, implementi IValidateOptions<T>. Riceve l'istanza delle opzioni e restituisce un risultato con messaggi dettagliati relativi agli errori.

public class PaymentOptionsValidator : IValidateOptions<PaymentOptions>
{
    public ValidateOptionsResult Validate(string? name, PaymentOptions opts)
    {
        var failures = new List<string>();

        if (opts.Provider == "Stripe" && string.IsNullOrWhiteSpace(opts.StripeSecretKey))
            failures.Add("StripeSecretKey is required when Provider is Stripe");

        if (opts.Provider == "PayPal" && string.IsNullOrWhiteSpace(opts.PayPalClientId))
            failures.Add("PayPalClientId is required when Provider is PayPal");

        if (opts.RetryCount < 0 || opts.RetryCount > 5)
            failures.Add("RetryCount must be between 0 and 5");

        return failures.Count == 0
            ? ValidateOptionsResult.Success
            : ValidateOptionsResult.Fail(failures);
    }
}

builder.Services.AddSingleton<IValidateOptions<PaymentOptions>, PaymentOptionsValidator>();

Opzioni denominate — concetto

Le opzioni denominate consentono di registrare più configurazioni dello stesso tipo di opzioni. Un caso d'uso comune consiste nell'avere più client HTTP in uscita, ciascuno con URL di base e timeout diversi.

// appsettings.json:
{
  "HttpClients": {
    "Orders": {
      "BaseUrl": "https://orders-service",
      "TimeoutSeconds": 30
    },
    "Inventory": {
      "BaseUrl": "https://inventory-service",
      "TimeoutSeconds": 10
    }
  }
}

public class HttpClientOptions
{
    public string BaseUrl { get; set; } = string.Empty;
    public int TimeoutSeconds { get; set; } = 30;
}

Registrazione delle opzioni denominate

Passi una stringa contenente il nome come primo argomento a Configure<T>. Utilizzi IOptionsMonitor<T>.Get(name) per risolvere un'istanza specifica.

// Register:
builder.Services.Configure<HttpClientOptions>("Orders",
    builder.Configuration.GetSection("HttpClients:Orders"));
builder.Services.Configure<HttpClientOptions>("Inventory",
    builder.Configuration.GetSection("HttpClients:Inventory"));

// Consume:
public class ApiGateway
{
    private readonly HttpClientOptions _orders;
    private readonly HttpClientOptions _inventory;

    public ApiGateway(IOptionsMonitor<HttpClientOptions> monitor)
    {
        _orders    = monitor.Get("Orders");
        _inventory = monitor.Get("Inventory");
    }

    // IOptions<T>.Value always returns the unnamed (default) instance
    // IOptionsMonitor<T>.Get(name) returns the named instance
}

Opzioni denominate con convalida

Convalidi singolarmente le opzioni denominate chiamando AddOptions<T>(name) per ogni registrazione denominata.

foreach (var clientName in new[] { "Orders", "Inventory", "Auth" })
{
    builder.Services
        .AddOptions<HttpClientOptions>(clientName)
        .BindConfiguration($"HttpClients:{clientName}")
        .ValidateDataAnnotations()
        .Validate(
            opts => Uri.IsWellFormedUriString(opts.BaseUrl, UriKind.Absolute),
            $"HttpClients:{clientName}:BaseUrl must be a valid absolute URI")
        .ValidateOnStart();
}

// If any named instance fails, the app refuses to start

API OptionsBuilder

OptionsBuilder<T>, restituito da AddOptions<T>(), è l'API fluente che concatena in modo ordinato tutti i passaggi di registrazione, associazione e convalida.

// Full OptionsBuilder chain:
builder.Services
    .AddOptions<DatabaseOptions>()        // create builder
    .BindConfiguration("Database")         // bind JSON section
    .Configure(opts =>                     // manual override
    {
        if (builder.Environment.IsDevelopment())
            opts.EnableDetailedErrors = true;
    })
    .PostConfigure(opts =>                 // runs after all Configure
    {
        opts.ConnectionString ??= "default-fallback";
    })
    .ValidateDataAnnotations()             // attribute rules
    .Validate(o => o.MaxPoolSize > 0,
              "MaxPoolSize must be positive")
    .ValidateOnStart();                    // eager validation

Caso reale: opzioni per le policy di retry

Una configurazione completa delle opzioni per i retry, con convalida tra campi e policy denominate per servizi diversi.

public class RetryOptions
{
    [Range(0, 10)] public int MaxAttempts { get; set; } = 3;
    [Range(100, 60000)] public int BaseDelayMs { get; set; } = 500;
    public bool UseExponentialBackoff { get; set; } = true;
    [Range(1, 120000)] public int MaxDelayMs { get; set; } = 30000;
}

foreach (var policy in new[] { "Database", "HttpClient", "MessageBus" })
{
    builder.Services
        .AddOptions<RetryOptions>(policy)
        .BindConfiguration($"RetryPolicies:{policy}")
        .Validate(o => !o.UseExponentialBackoff || o.MaxDelayMs > o.BaseDelayMs,
                  "MaxDelayMs must exceed BaseDelayMs when using exponential backoff")
        .ValidateOnStart();
}

Verifica rapida

Qual è il vantaggio di chiamare ValidateOnStart() durante la registrazione delle opzioni?

Riepilogo: convalida delle opzioni e opzioni denominate

Concetti chiave:

  • ValidateOnStart(): l'errore viene rilevato all'avvio anziché al primo utilizzo
  • DataAnnotations: [Required], [Range] e così via + ValidateDataAnnotations()
  • Validate(Func, message): lambda inline per le regole tra proprietà
  • IValidateOptions<T>: convalida programmatica completa con più errori
  • Opzioni denominate: Configure<T>(name, ...) + IOptionsMonitor<T>.Get(name)
  • OptionsBuilder<T>: API fluente per concatenare tutti i passaggi di registrazione

Domande Frequenti

La lezione «Convalida delle opzioni e opzioni denominate» è gratuita?

Sì — il testo completo di «Convalida delle opzioni e opzioni denominate» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Convalida delle opzioni e opzioni denominate»?

Convalidi le opzioni all'avvio con DataAnnotations o FluentValidation e usi opzioni denominate per più istanze. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Convalida delle opzioni e opzioni denominate»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Fonti e provider di configurazione
  2. Opzioni fortemente tipizzate con IOptions
  3. Convalida delle opzioni e opzioni denominate
  4. Gestione dei segreti
← Torna a C# Academy