본문으로 건너뛰기
cccskills
무료GitHub에 공개

dotnet-csharp-configuration

Configures Options pattern, user secrets, and feature flags. IOptions<T>, FeatureManagement.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구Jira
설치 방법 보기

포함된 파일(1)

  • SKILL.md14.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

dotnet-csharp-configuration

Configuration patterns for .NET applications using Microsoft.Extensions.Configuration and Microsoft.Extensions.Options. Covers the Options pattern (IOptions<T>, IOptionsMonitor<T>, IOptionsSnapshot<T>), validation, user secrets, environment-based configuration, and feature flags with Microsoft.FeatureManagement.

Scope

  • Options pattern (IOptions<T>, IOptionsMonitor<T>, IOptionsSnapshot<T>)
  • Options validation and ValidateOnStart
  • User secrets and environment-based configuration
  • Feature flags with Microsoft.FeatureManagement
  • Configuration source precedence

Out of scope

  • DI container mechanics and service lifetimes -- see [skill:dotnet-csharp-dependency-injection]
  • EditorConfig and analyzer rule configuration -- see [skill:dotnet-editorconfig]
  • Structured logging pipeline configuration -- see [skill:dotnet-structured-logging]

Cross-references: [skill:dotnet-csharp-dependency-injection] for service registration patterns, [skill:dotnet-csharp-coding-standards] for naming conventions.


Configuration Sources and Precedence

Default configuration sources in WebApplication.CreateBuilder (last wins):

  1. appsettings.json
  2. appsettings.{Environment}.json
  3. User secrets (Development only)
  4. Environment variables
  5. Command-line arguments

var builder = WebApplication.CreateBuilder(args);
// Sources above are loaded automatically. Add custom sources:
builder.Configuration.AddJsonFile("features.json", optional: true, reloadOnChange: true);

```csharp

---

## Options Pattern

Bind configuration sections to strongly typed classes and inject them via DI.

### Defining Options Classes

```csharp

public sealed class SmtpOptions
{
    public const string SectionName = "Smtp";

    public string Host { get; set; } = "";
    public int Port { get; set; } = 587;
    public string FromAddress { get; set; } = "";
    public bool UseSsl { get; set; } = true;
}

```text

> Options classes use `{ get; set; }` (not `init`) because the configuration binder and `PostConfigure` need to mutate
> properties. Use `[Required]` via data annotations for mandatory fields instead.

### Registration

```csharp

builder.Services
    .AddOptions<SmtpOptions>()
    .BindConfiguration(SmtpOptions.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart();

```text

### `appsettings.json`

```json

{
  "Smtp": {
    "Host": "smtp.example.com",
    "Port": 587,
    "FromAddress": "noreply@example.com",
    "UseSsl": true
  }
}

```text

---

## Options Interfaces

| Interface             | Lifetime  | Reload Behavior                   | Use Case                        |
| --------------------- | --------- | --------------------------------- | ------------------------------- |
| `IOptions<T>`         | Singleton | Never reloads after startup       | Static config, most services    |
| `IOptionsSnapshot<T>` | Scoped    | Reloads per request/scope         | Per-request config in ASP.NET   |
| `IOptionsMonitor<T>`  | Singleton | Live reload + change notification | Singletons, background services |

### Injection Examples

```csharp

// Static -- most common, singleton-safe
public sealed class EmailService(IOptions<SmtpOptions> options)
{
    private readonly SmtpOptions _smtp = options.Value;

    public Task SendAsync(string to, string subject, string body,
        CancellationToken ct = default)
    {
        // Use _smtp.Host, _smtp.Port, etc.
        return Task.CompletedTask;
    }
}

// Live reload in singletons -- monitors config file changes
public sealed class FeatureService(IOptionsMonitor<FeatureOptions> monitor)
{
    public bool IsEnabled(string feature)
        => monitor.CurrentValue.EnabledFeatures.Contains(feature);
}

// Per-request in scoped services -- reads latest config each request
public sealed class PricingService(IOptionsSnapshot<PricingOptions> snapshot)
{
    public decimal GetMarkup() => snapshot.Value.MarkupPercent;
}

```text

### Change Notifications with `IOptionsMonitor<T>`

```csharp

public sealed class CacheService : IDisposable
{
    private readonly IDisposable? _changeListener;
    private CacheOptions _current;

    public CacheService(IOptionsMonitor<CacheOptions> monitor)
    {
        _current = monitor.CurrentValue;
        _changeListener = monitor.OnChange(updated =>
        {
            _current = updated;
            // React to config change -- flush cache, resize pool, etc.
        });
    }

    public void Dispose() => _changeListener?.Dispose();
}

```text

---

## Options Validation

### Data Annotations

```csharp

using System.ComponentModel.DataAnnotations;

public sealed class SmtpOptions
{
    public const string SectionName = "Smtp";

    [Required, MinLength(1)]
    public string Host { get; set; } = "";

    [Range(1, 65535)]
    public int Port { get; set; } = 587;

    [Required, EmailAddress]
    public string FromAddress { get; set; } = "";
}

builder.Services
    .AddOptions<SmtpOptions>()
    .BindConfiguration(SmtpOptions.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart(); // Fail fast at startup, not on first use

```text

### `IValidateOptions<T>` (Complex Validation)

Use when validation logic requires cross-property checks or external dependencies.

```csharp

public sealed class SmtpOptionsValidator : IValidateOptions<SmtpOptions>
{
    public ValidateOptionsResult Validate(string? name, SmtpOptions options)
    {
        var failures = new List<string>();

        if (options.UseSsl && options.Port == 25)
        {
            failures.Add("Port 25 does not support SSL. Use 465 or 587.");
        }

        if (string.IsNullOrWhiteSpace(options.Host))
        {
            failures.Add("SMTP host is required.");
        }

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

// Register the validator
builder.Services.AddSingleton<IValidateOptions<SmtpOptions>, SmtpOptionsValidator>();

```text

### `ValidateOnStart` (Fail Fast)

Always use `.ValidateOnStart()` to surface configuration errors at startup instead of at first resolution. Without it,
invalid config only throws when `IOptions<T>.Value` is first accessed.

---

## User Secrets (Development)

Store sensitive values outside source control during development.

```bash

# Initialize (once per project)
dotnet user-secrets init

# Set values
dotnet user-secrets set "Smtp:Host" "smtp.example.com"
dotnet user-secrets set "ConnectionStrings:Default" "Server=..."

# List all secrets
dotnet user-secrets list

# Clear all
dotnet user-secrets clear

```text

User secrets are stored in `~/.microsoft/usersecrets/<UserSecretsId>/secrets.json` and override `appsettings.json`
values in Development.

**Key rules:**

- Never use user secrets in production -- use environment variables, Azure Key Vault, or other vault providers
- User secrets are loaded automatically when `ASPNETCORE_ENVIRONMENT=Development`
- For non-web hosts, explicitly add: `builder.Configuration.AddUserSecrets<Program>()`

---

## Environment-Based Configuration

### Environment Variables

```csharp

// Hierarchical keys use __ (double underscore) as separator
// Environment variable: Smtp__Host=smtp.prod.com
// Maps to: configuration["Smtp:Host"]

```csharp

### Per-Environment Files

```text

appsettings.json                 # Base (all environments)
appsettings.Development.json     # Overrides for dev
appsettings.Staging.json         # Overrides for staging
appsettings.Production.json      # Overrides for prod

```json

```csharp

// Set environment via ASPNETCORE_ENVIRONMENT or DOTNET_ENVIRONMENT
// Defaults to "Production" if not set
var env = builder.Environment.EnvironmentName; // "Development", "Staging", "Production"

```csharp

### Conditional Service Registration

```csharp

if (builder.Environment.IsDevelopment())
{
    builder.Services.AddSingleton<IEmailSender, ConsoleEmailSender>();
}
else
{
    builder.Services.AddSingleton<IEmailSender, SmtpEmailSender>();
}

```text

---

## Feature Flags with Microsoft.FeatureManagement

`Microsoft.FeatureManagement.AspNetCore` provides structured feature flag support with filters, targeting, and gradual
rollout.

### Setup

```bash

dotnet add package Microsoft.FeatureManagement.AspNetCore

```bash

```csharp

builder.Services.AddFeatureManagement();

```csharp

### Configuration

```json

{
  "FeatureManagement": {
    "NewDashboard": true,
    "BetaSearch": {
      "EnabledFor": [
        {
          "Name": "Percentage",
          "Parameters": { "Value": 50 }
        }
      ]
    },
    "DarkMode": {
      "EnabledFor": [
        {
          "Name": "Targeting",
          "Parameters": {
            "Audience": {
              "Users": ["alice@example.com"],
              "Groups": [{ "Name": "Beta", "RolloutPercentage": 100 }],
              "DefaultRolloutPercentage": 0
            }
          }
        }
      ]
    }
  }
}

```text

### Usage in Code

```csharp

// Inject IFeatureManager
public sealed class DashboardController(IFeatureManager featureManager) : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> Get(CancellationToken ct = default)
    {
        if (await featureManager.IsEnabledAsync("NewDashboard"))
        {
            return Ok(new { version = "v2", dashboard = "new" });
        }

        return Ok(new { version = "v1", dashboard = "legacy" });
    }
}

```text

### Feature Gate Attribute

```csharp

// Entire endpoint gated on feature flag
[FeatureGate("BetaSearch")]
[HttpGet("search")]
public async Task<IActionResult> Search(string query, CancellationToken ct = default)
{
    var results = await _searchService.SearchAsync(query, ct);
    return Ok(results);
}

```text

### Feature Filters

| Filter       | Purpose                                                  |
| ------------ | -------------------------------------------------------- |
| `Percentage` | Enable for N% of requests (random)                       |
| `TimeWindow` | Enable between start/end dates                           |
| `Targeting`  | Enable for specific users, groups, or rollout percentage |
| Custom       | Implement `IFeatureFilter` for domain-specific logic     |

### Custom Feature Filter

```csharp

[FilterAlias("Browser")]
public sealed class BrowserFeatureFilter(IHttpContextAccessor accessor) : IFeatureFilter
{
    public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
    {
        var userAgent = accessor.HttpContext?.Request.Headers.UserAgent.ToString() ?? "";
        var settings = context.Parameters.Get<BrowserFilterSettings>();

        return Task.FromResult(
            settings?.AllowedBrowsers?.Any(b =>
                userAgent.Contains(b, StringComparison.OrdinalIgnoreCase)) ?? false);
    }
}

public sealed class BrowserFilterSettings
{
    public string[] AllowedBrowsers { get; init; } = [];
}

// Register
builder.Services.AddFeatureManagement()
    .AddFeatureFilter<BrowserFeatureFilter>();

```text

---

## Named Options

Use named options when you need multiple instances of the same options type (e.g., multiple API clients).

```csharp

// Registration with names
builder.Services
    .AddOptions<ApiClientOptions>("GitHub")
    .BindConfiguration("ApiClients:GitHub");

builder.Services
    .AddOptions<ApiClientOptions>("Jira")
    .BindConfiguration("ApiClients:Jira");

// Resolution via IOptionsSnapshot<T> or IOptionsMonitor<T>
public sealed class ApiClientFactory(IOptionsSnapshot<ApiClientOptions> snapshot)
{
    public HttpClient CreateFor(string name)
    {
        var options = snapshot.Get(name); // "GitHub" or "Jira"
        return new HttpClient { BaseAddress = new Uri(options.BaseUrl) };
    }
}

```text

---

## Post-Configuration

Apply defaults or overrides after all configuration sources have been processed.

```csharp

builder.Services.PostConfigure<SmtpOptions>(options =>
{
    // Ensure a default port if none specified
    if (options.Port == 0)
    {
        options.Port = options.UseSsl ? 465 : 25;
    }
});

```text

---

## Testing Configuration

```csharp

[Fact]
public void SmtpOptions_Validates_InvalidPort()
{
    var options = new SmtpOptions
    {
        Host = "smtp.example.com",
        FromAddress = "test@example.com",
        Port = 25,
        UseSsl = true
    };

    var validator = new SmtpOptionsValidator();
    var result = validator.Validate(null, options);

    Assert.True(result.Failed);
    Assert.Contains("Port 25 does not support SSL", result.FailureMessage);
}

[Fact]
public void Configuration_BindsCorrectly()
{
    var config = new ConfigurationBuilder()
        .AddInMemoryCollection(new Dictionary<string, string?>
        {
            ["Smtp:Host"] = "smtp.test.com",
            ["Smtp:Port"] = "465",
            ["Smtp:FromAddress"] = "test@test.com",
        })
        .Build();

    var options = new SmtpOptions();
    config.GetSection("Smtp").Bind(options);

    Assert.Equal("smtp.test.com", options.Host);
    Assert.Equal(465, options.Port);
}

```text

---



## Code Navigation (Serena MCP)

**Primary approach:** Use Serena symbol operations for efficient code navigation:

1. **Find definitions**: `serena_find_symbol` instead of text search
2. **Understand structure**: `serena_get_symbols_overview` for file organization
3. **Track references**: `serena_find_referencing_symbols` for impact analysis
4. **Precise edits**: `serena_replace_symbol_body` for clean modifications

**When to use Serena vs traditional tools:**
- ✅ **Use Serena**: Navigation, refactoring, dependency analysis, precise edits
- ✅ **Use Read/Grep**: Reading full files, pattern matching, simple text operations
- ✅ **Fallback**: If Serena unavailable, traditional tools work fine

**Example workflow:**
```text
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"

# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"
```
## References

- [Options pattern in .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/options)
- [Configuration in .NET](https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration)
- [User secrets in development](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets)
- [Feature management in .NET](https://learn.microsoft.com/en-us/azure/azure-app-configuration/use-feature-flags-dotnet-core)
- [IValidateOptions](https://learn.microsoft.com/en-us/dotnet/core/extensions/options#options-validation)
- [.NET Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/)

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Patterns and techniques for evaluating and improving AI agent outputs.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

Use when user requests research requiring multiple sources, comprehensive analysis, or synthesis across topics - technical research, domain knowledge gathering, market analysis, or learning about complex subjects

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

deep-wiki

무료

AI-powered wiki generation for code repositories with commands, agents, and skills

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

Use when building .NET 10 or C# 14 applications; when using minimal APIs, modular monolith patterns, or feature folders; when implementing HTTP resilience, Options pattern, Channels, or validation; when seeing outdated patterns like old extension method syntax

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

Implements accessible .NET UI. SemanticProperties, ARIA, AutomationPeer, testing per platform.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rudironsoni/Synaxis22026년 3월 17일 업데이트

rudironsoni의 스킬 모두 보기

이 스킬의 문제 신고