# Lang Dotnet Dev

> Foundational .NET patterns covering runtime, project structure, dependency injection, configuration, metaprogramming, and cross-platform development. Use when working with .NET projects or CLI tools. This is the entry point for .NET development.

- Skill: `arustydev/lang-dotnet-dev` (Agent Skill)
- Install (CLI): `npx skillmds@latest add arustydev/lang-dotnet-dev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/arustydev/lang-dotnet-dev/raw
- Safety review: pending (external: skill-scanner PASS, skillspector WARNING)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: aRustyDev (https://skillmd.com/u/arustydev)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/arustydev/lang-dotnet-dev

---


# .NET Development Skill

Comprehensive foundational patterns for .NET development, covering runtime fundamentals, project structure, dependency injection, configuration management, logging, middleware, metaprogramming, and cross-platform development.

## Table of Contents

- [.NET Runtime and SDK](#net-runtime-and-sdk)
- [Project Structure](#project-structure)
- [CLI Tools](#cli-tools)
- [Project Types](#project-types)
- [Dependency Injection](#dependency-injection)
- [Configuration](#configuration)
- [Logging](#logging)
- [Middleware Patterns](#middleware-patterns)
- [NuGet Packages](#nuget-packages)
- [Cross-Platform Development](#cross-platform-development)
- [Metaprogramming](#metaprogramming)
- [Best Practices](#best-practices)
- [Cross-Cutting Patterns](#cross-cutting-patterns)

## .NET Runtime and SDK

### Runtime Components

The .NET runtime provides the execution environment for .NET applications.

```csharp
// Runtime information
using System;
using System.Runtime.InteropServices;

public class RuntimeInfo
{
    public static void DisplayRuntimeInfo()
    {
        // Framework description
        Console.WriteLine($"Framework: {RuntimeInformation.FrameworkDescription}");

        // OS description
        Console.WriteLine($"OS: {RuntimeInformation.OSDescription}");

        // Architecture
        Console.WriteLine($"Architecture: {RuntimeInformation.OSArchitecture}");

        // Process architecture
        Console.WriteLine($"Process: {RuntimeInformation.ProcessArchitecture}");

        // Runtime identifier
        Console.WriteLine($"RID: {RuntimeInformation.RuntimeIdentifier}");
    }
}
```

### SDK vs Runtime

```bash
# Check installed SDKs
dotnet --list-sdks

# Check installed runtimes
dotnet --list-runtimes

# Check SDK version
dotnet --version

# Display all info
dotnet --info
```

### Global.json Configuration

```json
{
  "sdk": {
    "version": "8.0.0",
    "rollForward": "latestMinor",
    "allowPrerelease": false
  }
}
```

```bash
# Create global.json with specific SDK version
dotnet new globaljson --sdk-version 8.0.100

# Roll forward policies:
# - patch: Use latest patch version
# - feature: Use latest feature band
# - minor: Use latest minor version
# - major: Use latest major version
# - latestPatch: Use latest patch (default)
# - latestMinor: Use latest minor
# - latestMajor: Use latest major
# - disable: Use exact version
```

### .NET Standard vs .NET

```csharp
// Target Framework Monikers (TFM)

// Modern .NET (cross-platform)
// <TargetFramework>net8.0</TargetFramework>
// <TargetFramework>net7.0</TargetFramework>
// <TargetFramework>net6.0</TargetFramework>

// .NET Standard (compatibility layer)
// <TargetFramework>netstandard2.1</TargetFramework>
// <TargetFramework>netstandard2.0</TargetFramework>

// .NET Framework (Windows only)
// <TargetFramework>net48</TargetFramework>
// <TargetFramework>net472</TargetFramework>

// Multi-targeting
// <TargetFrameworks>net8.0;net6.0;netstandard2.0</TargetFrameworks>

// Conditional compilation
#if NET8_0_OR_GREATER
    // Code for .NET 8+
#elif NET6_0_OR_GREATER
    // Code for .NET 6+
#elif NETSTANDARD2_0
    // Code for .NET Standard 2.0
#endif
```

### Runtime Configuration

```json
// runtimeconfig.json
{
  "runtimeOptions": {
    "tfm": "net8.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "8.0.0"
    },
    "configProperties": {
      "System.GC.Server": true,
      "System.GC.Concurrent": true,
      "System.GC.RetainVM": true,
      "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
    }
  }
}
```

## Project Structure

### Project File (.csproj)

```xml
<Project Sdk="Microsoft.NET.Sdk">

  <!-- Basic Properties -->
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <OutputType>Exe</OutputType>
    <RootNamespace>MyCompany.MyProduct</RootNamespace>
    <AssemblyName>MyProduct</AssemblyName>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <!-- Package Properties -->
  <PropertyGroup>
    <PackageId>MyCompany.MyProduct</PackageId>
    <Version>1.0.0</Version>
    <Authors>Your Name</Authors>
    <Company>MyCompany</Company>
    <Product>MyProduct</Product>
    <Description>Package description</Description>
    <Copyright>Copyright © 2024</Copyright>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <PackageProjectUrl>https://github.com/mycompany/myproduct</PackageProjectUrl>
    <RepositoryUrl>https://github.com/mycompany/myproduct</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
    <PackageTags>tag1;tag2;tag3</PackageTags>
    <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
  </PropertyGroup>

  <!-- Build Properties -->
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <WarningsAsErrors />
    <NoWarn>CS1591</NoWarn>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

  <!-- Debug Configuration -->
  <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
    <DebugType>full</DebugType>
    <DebugSymbols>true</DebugSymbols>
    <Optimize>false</Optimize>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
  </PropertyGroup>

  <!-- Release Configuration -->
  <PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <DebugType>pdbonly</DebugType>
    <DebugSymbols>true</DebugSymbols>
    <Optimize>true</Optimize>
    <DefineConstants>TRACE</DefineConstants>
  </PropertyGroup>

  <!-- Package References -->
  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
  </ItemGroup>

  <!-- Project References -->
  <ItemGroup>
    <ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
  </ItemGroup>

  <!-- Conditional Package References -->
  <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
    <PackageReference Include="System.Text.Json" Version="8.0.0" />
  </ItemGroup>

  <!-- Content Files -->
  <ItemGroup>
    <Content Include="appsettings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="appsettings.*.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <DependentUpon>appsettings.json</DependentUpon>
    </Content>
  </ItemGroup>

  <!-- Embedded Resources -->
  <ItemGroup>
    <EmbeddedResource Include="Resources\*.txt" />
  </ItemGroup>

  <!-- None Items -->
  <ItemGroup>
    <None Include="README.md" Pack="true" PackagePath="\" />
    <None Include="LICENSE" Pack="true" PackagePath="\" />
  </ItemGroup>

</Project>
```

### Solution File (.sln)

```bash
# Create a new solution
dotnet new sln -n MySolution

# Add projects to solution
dotnet sln add src/MyApp/MyApp.csproj
dotnet sln add src/MyLibrary/MyLibrary.csproj
dotnet sln add tests/MyApp.Tests/MyApp.Tests.csproj

# List projects in solution
dotnet sln list

# Remove project from solution
dotnet sln remove src/OldProject/OldProject.csproj
```

### Directory Structure

```
MySolution/
├── .gitignore
├── .editorconfig
├── global.json
├── MySolution.sln
├── Directory.Build.props         # Shared properties for all projects
├── Directory.Build.targets       # Shared targets for all projects
├── Directory.Packages.props      # Central package management
├── src/
│   ├── MyApp/
│   │   ├── MyApp.csproj
│   │   ├── Program.cs
│   │   ├── appsettings.json
│   │   ├── appsettings.Development.json
│   │   └── Controllers/
│   │       └── WeatherController.cs
│   └── MyLibrary/
│       ├── MyLibrary.csproj
│       ├── Class1.cs
│       └── Interfaces/
│           └── IMyService.cs
├── tests/
│   ├── MyApp.Tests/
│   │   ├── MyApp.Tests.csproj
│   │   └── UnitTest1.cs
│   └── MyLibrary.Tests/
│       ├── MyLibrary.Tests.csproj
│       └── Class1Tests.cs
├── docs/
│   └── README.md
└── tools/
    └── build.ps1
```

### Directory.Build.props

```xml
<Project>

  <!-- Common properties for all projects -->
  <PropertyGroup>
    <LangVersion>latest</LangVersion>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>

  <!-- Version properties -->
  <PropertyGroup>
    <VersionPrefix>1.0.0</VersionPrefix>
    <VersionSuffix Condition="'$(Configuration)' == 'Debug'">dev</VersionSuffix>
  </PropertyGroup>

  <!-- Source Link (for debugging) -->
  <PropertyGroup>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
    <IncludeSymbols>true</IncludeSymbols>
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
  </ItemGroup>

</Project>
```

### Directory.Packages.props (Central Package Management)

```xml
<Project>

  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup>
    <!-- Common packages -->
    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
    <PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
    <PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />

    <!-- Test packages -->
    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
    <PackageVersion Include="xunit" Version="2.6.2" />
    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
    <PackageVersion Include="coverlet.collector" Version="6.0.0" />
  </ItemGroup>

</Project>
```

## CLI Tools

### dotnet new

```bash
# List available templates
dotnet new list

# Common templates
dotnet new console -n MyConsoleApp
dotnet new classlib -n MyLibrary
dotnet new web -n MyWebApp
dotnet new webapi -n MyApi
dotnet new mvc -n MyMvcApp
dotnet new razor -n MyRazorApp
dotnet new blazorserver -n MyBlazorApp
dotnet new blazorwasm -n MyBlazorWasmApp
dotnet new worker -n MyWorkerService
dotnet new xunit -n MyTests
dotnet new nunit -n MyNUnitTests
dotnet new mstest -n MyMSTests

# Create with options
dotnet new console -n MyApp -f net8.0 -o src/MyApp
dotnet new webapi -n MyApi --use-controllers --auth Individual

# Install templates
dotnet new install Amazon.Lambda.Templates
dotnet new install Microsoft.AspNetCore.SpaTemplates

# Uninstall templates
dotnet new uninstall Amazon.Lambda.Templates

# Create from template with language
dotnet new console -n MyApp -lang "F#"
dotnet new classlib -n MyLib -lang "VB"
```

### dotnet build

```bash
# Build project
dotnet build

# Build with configuration
dotnet build -c Release
dotnet build --configuration Debug

# Build specific framework
dotnet build -f net8.0

# Build with no dependencies
dotnet build --no-dependencies

# Build with no restore
dotnet build --no-restore

# Build with specific runtime
dotnet build -r win-x64
dotnet build -r linux-x64
dotnet build -r osx-x64
dotnet build -r osx-arm64

# Build verbosity
dotnet build -v quiet
dotnet build -v minimal
dotnet build -v normal
dotnet build -v detailed
dotnet build -v diagnostic

# Build and output to directory
dotnet build -o ./output
```

### dotnet run

```bash
# Run project
dotnet run

# Run with configuration
dotnet run -c Release

# Run with framework
dotnet run -f net8.0

# Run with arguments
dotnet run -- arg1 arg2
dotnet run --project ./src/MyApp/MyApp.csproj

# Run with environment
dotnet run --environment Production
ASPNETCORE_ENVIRONMENT=Production dotnet run

# Run without build
dotnet run --no-build

# Run without restore
dotnet run --no-restore

# Run with launch profile
dotnet run --launch-profile "Production"
```

### dotnet publish

```bash
# Publish for deployment
dotnet publish

# Publish with configuration
dotnet publish -c Release

# Publish with runtime (self-contained)
dotnet publish -r win-x64 --self-contained
dotnet publish -r linux-x64 -p:PublishSingleFile=true
dotnet publish -r osx-arm64 --self-contained

# Publish framework-dependent
dotnet publish -r win-x64 --self-contained false

# Publish with trimming
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true
dotnet publish -c Release -p:PublishTrimmed=true -p:TrimMode=full

# Publish as single file
dotnet publish -r win-x64 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfContained=true

# Publish ReadyToRun
dotnet publish -c Release -r win-x64 -p:PublishReadyToRun=true

# Publish to folder
dotnet publish -o ./publish

# Publish with version
dotnet publish -p:Version=1.2.3
```

### dotnet restore

```bash
# Restore dependencies
dotnet restore

# Restore with specific source
dotnet restore --source https://api.nuget.org/v3/index.json
dotnet restore -s ./local-packages

# Restore for specific runtime
dotnet restore -r win-x64

# Restore with locked mode (use packages.lock.json)
dotnet restore --locked-mode

# Force evaluation of all dependencies
dotnet restore --force

# Restore without cache
dotnet restore --no-cache
```

### dotnet test

```bash
# Run tests
dotnet test

# Run tests with configuration
dotnet test -c Release

# Run tests with verbosity
dotnet test -v normal
dotnet test --verbosity detailed

# Run tests with filter
dotnet test --filter FullyQualifiedName~MyNamespace
dotnet test --filter Category=Unit
dotnet test --filter "Priority=1|Category=Smoke"

# Run tests with coverage
dotnet test --collect:"XPlat Code Coverage"
dotnet test /p:CollectCoverage=true

# Run tests with logger
dotnet test --logger "trx;LogFileName=test-results.trx"
dotnet test --logger "html;LogFileName=test-results.html"

# Run tests without build
dotnet test --no-build
```

### dotnet clean

```bash
# Clean build outputs
dotnet clean

# Clean with configuration
dotnet clean -c Release

# Clean specific framework
dotnet clean -f net8.0

# Clean and remove obj folder
dotnet clean
rm -rf **/obj
```

### dotnet pack

```bash
# Create NuGet package
dotnet pack

# Pack with configuration
dotnet pack -c Release

# Pack with version
dotnet pack -p:Version=1.2.3

# Pack without build
dotnet pack --no-build

# Pack to output directory
dotnet pack -o ./packages

# Include symbols
dotnet pack -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
```

### dotnet add/remove

```bash
# Add package
dotnet add package Microsoft.Extensions.Logging
dotnet add package Newtonsoft.Json --version 13.0.3

# Add package from source
dotnet add package MyPackage --source ./local-packages

# Add package to specific project
dotnet add src/MyApp/MyApp.csproj package Serilog

# Remove package
dotnet remove package Newtonsoft.Json

# Add project reference
dotnet add reference ../MyLibrary/MyLibrary.csproj

# Remove project reference
dotnet remove reference ../MyLibrary/MyLibrary.csproj
```

### dotnet list

```bash
# List packages
dotnet list package
dotnet list package --outdated
dotnet list package --deprecated
dotnet list package --vulnerable

# List project references
dotnet list reference

# List projects in solution
dotnet sln list
```

### dotnet tool

```bash
# Install global tool
dotnet tool install -g dotnet-ef
dotnet tool install -g dotnet-format

# Install local tool
dotnet new tool-manifest
dotnet tool install dotnet-ef

# List tools
dotnet tool list -g
dotnet tool list

# Update tool
dotnet tool update -g dotnet-ef
dotnet tool update dotnet-ef

# Uninstall tool
dotnet tool uninstall -g dotnet-ef
dotnet tool uninstall dotnet-ef

# Run tool
dotnet ef --version
dotnet format
```

## Project Types

### Console Application

```csharp
// Program.cs (Top-level statements)
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var builder = Host.CreateApplicationBuilder(args);

// Configure services
builder.Services.AddTransient<IMyService, MyService>();
builder.Services.AddLogging(logging =>
{
    logging.AddConsole();
    logging.AddDebug();
});

var host = builder.Build();

// Run application
var service = host.Services.GetRequiredService<IMyService>();
await service.RunAsync();

await host.RunAsync();

// Service implementation
public interface IMyService
{
    Task RunAsync();
}

public class MyService : IMyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public async Task RunAsync()
    {
        _logger.LogInformation("Service started at {Time}", DateTime.UtcNow);
        await Task.Delay(1000);
        _logger.LogInformation("Service completed at {Time}", DateTime.UtcNow);
    }
}
```

### Class Library

```csharp
// MyLibrary.csproj
/*
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>
*/

namespace MyLibrary;

/// <summary>
/// Calculator service for basic arithmetic operations.
/// </summary>
public class Calculator
{
    /// <summary>
    /// Adds two numbers.
    /// </summary>
    /// <param name="a">First number</param>
    /// <param name="b">Second number</param>
    /// <returns>Sum of a and b</returns>
    public int Add(int a, int b) => a + b;

    /// <summary>
    /// Subtracts two numbers.
    /// </summary>
    /// <param name="a">First number</param>
    /// <param name="b">Second number</param>
    /// <returns>Difference of a and b</returns>
    public int Subtract(int a, int b) => a - b;
}
```

### Web API

```csharp
// Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseCors();
app.UseAuthorization();
app.MapControllers();

app.Run();

// Controllers/WeatherController.cs
using Microsoft.AspNetCore.Mvc;

namespace MyApi.Controllers;

[ApiController]
[Route("api/[controller]")]
public class WeatherController : ControllerBase
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild",
        "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherController> _logger;

    public WeatherController(ILogger<WeatherController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IEnumerable<WeatherForecast> Get()
    {
        _logger.LogInformation("Getting weather forecast");

        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }

    [HttpGet("{id}")]
    public ActionResult<WeatherForecast> GetById(int id)
    {
        if (id < 1 || id > 5)
            return NotFound();

        return new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now.AddDays(id)),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        };
    }

    [HttpPost]
    public ActionResult<WeatherForecast> Create(WeatherForecast forecast)
    {
        _logger.LogInformation("Creating weather forecast");
        return CreatedAtAction(nameof(GetById), new { id = 1 }, forecast);
    }
}

public record WeatherForecast
{
    public DateOnly Date { get; init; }
    public int TemperatureC { get; init; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; init; }
}
```

### Worker Service

```csharp
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();

var host = builder.Build();
await host.RunAsync();

// Worker.cs
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(10000, stoppingToken);
        }
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Worker starting");
        await base.StartAsync(cancellationToken);
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Worker stopping");
        await base.StopAsync(cancellationToken);
    }
}
```

## Dependency Injection

### Service Lifetimes

```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

// Transient: Created each time requested
builder.Services.AddTransient<ITransientService, TransientService>();

// Scoped: Created once per scope (request in web apps)
builder.Services.AddScoped<IScopedService, ScopedService>();

// Singleton: Created once for application lifetime
builder.Services.AddSingleton<ISingletonService, SingletonService>();

// Singleton instance
builder.Services.AddSingleton<IConfigService>(new ConfigService("config"));

// Singleton factory
builder.Services.AddSingleton<IFactoryService>(sp =>
{
    var logger = sp.GetRequiredService<ILogger<FactoryService>>();
    return new FactoryService(logger);
});

var host = builder.Build();
```

### Service Registration Patterns

```csharp
using Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    // Extension method for clean registration
    public static IServiceCollection AddMyServices(
        this IServiceCollection services)
    {
        services.AddTransient<IMyService, MyService>();
        services.AddScoped<IDataService, DataService>();
        services.AddSingleton<ICacheService, CacheService>();

        return services;
    }

    // Generic registration
    public static IServiceCollection AddRepository<TEntity>(
        this IServiceCollection services)
        where TEntity : class
    {
        services.AddScoped<IRepository<TEntity>, Repository<TEntity>>();
        return services;
    }

    // Conditional registration
    public static IServiceCollection AddConditionalService(
        this IServiceCollection services,
        bool condition)
    {
        if (condition)
        {
            services.AddTransient<IService, ServiceA>();
        }
        else
        {
            services.AddTransient<IService, ServiceB>();
        }

        return services;
    }

    // TryAdd - only adds if not already registered
    public static IServiceCollection AddDefaultServices(
        this IServiceCollection services)
    {
        services.TryAddTransient<IMyService, MyService>();
        services.TryAddScoped<IDataService, DataService>();
        services.TryAddSingleton<ICacheService, CacheService>();

        return services;
    }

    // Replace service
    public static IServiceCollection ReplaceService(
        this IServiceCollection services)
    {
        services.Replace(ServiceDescriptor.Transient<IMyService, NewMyService>());
        return services;
    }

    // Remove service
    public static IServiceCollection RemoveService(
        this IServiceCollection services)
    {
        var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(IMyService));
        if (descriptor != null)
        {
            services.Remove(descriptor);
        }

        return services;
    }
}
```

### Constructor Injection

```csharp
public interface IEmailService
{
    Task SendEmailAsync(string to, string subject, string body);
}

public interface ILogger<T>
{
    void LogInformation(string message);
}

public class UserService
{
    private readonly IEmailService _emailService;
    private readonly ILogger<UserService> _logger;
    private readonly IConfiguration _configuration;

    // Constructor injection
    public UserService(
        IEmailService emailService,
        ILogger<UserService> logger,
        IConfiguration configuration)
    {
        _emailService = emailService ?? throw new ArgumentNullException(nameof(emailService));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
    }

    public async Task CreateUserAsync(string email, string name)
    {
        _logger.LogInformation("Creating user {Name}", name);

        var welcomeMessage = _configuration["Messages:Welcome"] ?? "Welcome!";
        await _emailService.SendEmailAsync(email, "Welcome", welcomeMessage);
    }
}
```

### Service Resolution

```csharp
using Microsoft.Extensions.DependencyInjection;

public class ServiceResolver
{
    private readonly IServiceProvider _serviceProvider;

    public ServiceResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void ResolveServices()
    {
        // Get required service (throws if not found)
        var requiredService = _serviceProvider.GetRequiredService<IMyService>();

        // Get service (returns null if not found)
        var optionalService = _serviceProvider.GetService<IOptionalService>();

        // Get all implementations
        var allServices = _serviceProvider.GetServices<IMyService>();

        // Create scope
        using var scope = _serviceProvider.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();

        // Resolve with keyed services (.NET 8+)
        var keyedService = _serviceProvider.GetRequiredKeyedService<ICache>("redis");
        var anotherKeyedService = _serviceProvider.GetKeyedService<ICache>("memory");
    }
}

// Keyed services (.NET 8+)
public static class KeyedServiceExtensions
{
    public static IServiceCollection AddKeyedServices(
        this IServiceCollection services)
    {
        services.AddKeyedSingleton<ICache, RedisCache>("redis");
        services.AddKeyedSingleton<ICache, MemoryCache>("memory");

        return services;
    }
}

public class ServiceUsingKeyedDependencies
{
    private readonly ICache _redisCache;
    private readonly ICache _memoryCache;

    public ServiceUsingKeyedDependencies(
        [FromKeyedServices("redis")] ICache redisCache,
        [FromKeyedServices("memory")] ICache memoryCache)
    {
        _redisCache = redisCache;
        _memoryCache = memoryCache;
    }
}
```

## Configuration

### appsettings.json

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=mydb;User Id=sa;Password=P@ssw0rd;",
    "Redis": "localhost:6379"
  },
  "AppSettings": {
    "ApplicationName": "My Application",
    "Version": "1.0.0",
    "Features": {
      "EnableFeatureA": true,
      "EnableFeatureB": false
    }
  },
  "Email": {
    "SmtpServer": "smtp.example.com",
    "SmtpPort": 587,
    "FromAddress": "noreply@example.com",
    "EnableSsl": true
  },
  "Cache": {
    "SlidingExpiration": "00:30:00",
    "AbsoluteExpiration": "01:00:00"
  },
  "ApiClients": {
    "ExternalApi": {
      "BaseUrl": "https://api.example.com",
      "Timeout": "00:00:30",
      "ApiKey": ""
    }
  }
}
```

### Environment-Specific Configuration

```json
// appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "Microsoft.AspNetCore": "Debug"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=mydb_dev;User Id=dev;Password=dev123;"
  }
}

// appsettings.Production.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft.AspNetCore": "Error"
    }
  }
}

// appsettings.Staging.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}
```

### Configuration Options Pattern

```csharp
// AppSettings.cs
public class AppSettings
{
    public const string SectionName = "AppSettings";

    public string ApplicationName { get; set; } = string.Empty;
    public string Version { get; set; } = string.Empty;
    public FeatureSettings Features { get; set; } = new();
}

public class FeatureSettings
{
    public bool EnableFeatureA { get; set; }
    public bool EnableFeatureB { get; set; }
}

public class EmailSettings
{
    public const string SectionName = "Email";

    public string SmtpServer { get; set; } = string.Empty;
    public int SmtpPort { get; set; }
    public string FromAddress { get; set; } = string.Empty;
    public bool EnableSsl { get; set; }
}

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// Bind configuration sections to strongly-typed options
builder.Services.Configure<AppSettings>(
    builder.Configuration.GetSection(AppSettings.SectionName));

builder.Services.Configure<EmailSettings>(
    builder.Configuration.GetSection(EmailSettings.SectionName));

// Using IOptions<T>
public class MyService
{
    private readonly AppSettings _appSettings;
    private readonly EmailSettings _emailSettings;

    public MyService(
        IOptions<AppSettings> appOptions,
        IOptions<EmailSettings> emailOptions)
    {
        _appSettings = appOptions.Value;
        _emailSettings = emailOptions.Value;
    }

    public void DoSomething()
    {
        Console.WriteLine(_appSettings.ApplicationName);
        Console.WriteLine(_emailSettings.SmtpServer);
    }
}

// Using IOptionsSnapshot<T> (reloads on change, scoped)
public class MyReloadableService
{
    private readonly IOptionsSnapshot<AppSettings> _appSettings;

    public MyReloadableService(IOptionsSnapshot<AppSettings> appSettings)
    {
        _appSettings = appSettings;
    }

    public void DoSomething()
    {
        // Gets current value (reloaded if config changed)
        Console.WriteLine(_appSettings.Value.ApplicationName);
    }
}

// Using IOptionsMonitor<T> (reloads on change, singleton)
public class MyMonitoredService
{
    private readonly IOptionsMonitor<AppSettings> _appSettings;

    public MyMonitoredService(IOptionsMonitor<AppSettings> appSettings)
    {
        _appSettings = appSettings;

        // Subscribe to changes
        _appSettings.OnChange(settings =>
        {
            Console.WriteLine("Configuration changed!");
        });
    }

    public void DoSomething()
    {
        Console.WriteLine(_appSettings.CurrentValue.ApplicationName);
    }
}
```

### Configuration Sources

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

// Clear default sources
builder.Configuration.Sources.Clear();

// Add configuration sources in order (later sources override earlier ones)
builder.Configuration
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
    .AddXmlFile("config.xml", optional: true, reloadOnChange: true)
    .AddIniFile("config.ini", optional: true, reloadOnChange: true)
    .AddEnvironmentVariables()
    .AddEnvironmentVariables(prefix: "MYAPP_")
    .AddCommandLine(args)
    .AddUserSecrets<Program>() // Development only
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["Key1"] = "Value1",
        ["Key2"] = "Value2"
    });

var host = builder.Build();
```

### User Secrets (Development)

```bash
# Initialize user secrets
dotnet user-secrets init

# Set secret
dotnet user-secrets set "ApiKey" "my-secret-key"
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;Database=mydb;"

# List secrets
dotnet user-secrets list

# Remove secret
dotnet user-secrets remove "ApiKey"

# Clear all secrets
dotnet user-secrets clear
```

### Environment Variables

```bash
# Windows (PowerShell)
$env:ASPNETCORE_ENVIRONMENT = "Development"
$env:ConnectionStrings__DefaultConnection = "Server=localhost;Database=mydb;"
$env:AppSettings__ApplicationName = "My App"

# Linux/macOS
export ASPNETCORE_ENVIRONMENT=Development
export ConnectionStrings__DefaultConnection="Server=localhost;Database=mydb;"
export AppSettings__ApplicationName="My App"

# Note: __ (double underscore) represents nested configuration sections
# AppSettings__Features__EnableFeatureA maps to AppSettings:Features:EnableFeatureA
```

## Logging

### ILogger Interface

```csharp
using Microsoft.Extensions.Logging;

public class MyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }

    public void DoWork()
    {
        // Log levels
        _logger.LogTrace("Trace message");
        _logger.LogDebug("Debug message");
        _logger.LogInformation("Information message");
        _logger.LogWarning("Warning message");
        _logger.LogError("Error message");
        _logger.LogCritical("Critical message");

        // Structured logging with parameters
        var userId = 123;
        var userName = "John Doe";
        _logger.LogInformation("User {UserId} logged in: {UserName}", userId, userName);

        // Exception logging
        try
        {
            throw new InvalidOperationException("Something went wrong");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred while processing user {UserId}", userId);
        }

        // Log with event ID
        _logger.LogInformation(new EventId(1001, "UserLogin"),
            "User {UserId} logged in", userId);

        // Conditional logging
        if (_logger.IsEnabled(LogLevel.Debug))
        {
            var expensiveData = GetExpensiveDebugData();
            _logger.LogDebug("Debug data: {Data}", expensiveData);
        }

        // Log scopes
        using (_logger.BeginScope("Processing user {UserId}", userId))
        {
            _logger.LogInformation("Starting process");
            _logger.LogInformation("Process step 1");
            _logger.LogInformation("Process step 2");
            _logger.LogInformation("Completed process");
        }
    }

    private string GetExpensiveDebugData() => "expensive data";
}
```

### Configure Logging

```csharp
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;

var builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders(); // Remove default providers

// Add providers
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.AddEventSourceLogger();

// Set minimum level
builder.Logging.SetMinimumLevel(LogLevel.Information);

// Add filtering
builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
builder.Logging.AddFilter("System", LogLevel.Warning);
builder.Logging.AddFilter("MyApp", LogLevel.Debug);

// Configure from appsettings.json
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));

var host = builder.Build();
```

### Logging Configuration in appsettings.json

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore": "Warning",
      "MyApp": "Debug"
    },
    "Console": {
      "IncludeScopes": true,
      "LogLevel": {
        "Default": "Information"
      }
    },
    "Debug": {
      "LogLevel": {
        "Default": "Debug"
      }
    }
  }
}
```

### Third-Party Logging (Serilog Example)

```csharp
// Install packages:
// dotnet add package Serilog.AspNetCore
// dotnet add package Serilog.Sinks.Console
// dotnet add package Serilog.Sinks.File
// dotnet add package Serilog.Sinks.Seq

using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Configure Serilog
builder.Host.UseSerilog((context, configuration) =>
{
    configuration
        .ReadFrom.Configuration(context.Configuration)
        .Enrich.FromLogContext()
        .Enrich.WithMachineName()
        .Enrich.WithThreadId()
        .WriteTo.Console()
        .WriteTo.File(
            path: "logs/log-.txt",
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 30)
        .WriteTo.Seq("http://localhost:5341");
});

var app = builder.Build();

// Add Serilog request logging
app.UseSerilogRequestLogging();

app.Run();
```

## Middleware Patterns

### Built-in Middleware

```csharp
using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Order matters! Middleware executes in order added

// Exception handling (should be first)
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

// HTTPS redirection
app.UseHttpsRedirection();

// Static files
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "StaticFiles")),
    RequestPath = "/files"
});

// Routing
app.UseRouting();

// CORS (must be after routing, before authorization)
app.UseCors();

// Authentication
app.UseAuthentication();

// Authorization (must be after authentication)
app.UseAuthorization();

// Response caching
app.UseResponseCaching();

// Response compression
app.UseResponseCompression();

// Session
app.UseSession();

// Endpoints (should be last)
app.MapControllers();
app.MapRazorPages();

app.Run();
```

### Custom Middleware (Inline)

```csharp
var app = builder.Build();

// Use method
app.Use(async (context, next) =>
{
    // Before next middleware
    Console.WriteLine($"Before: {context.Request.Path}");

    await next(context);

    // After next middleware
    Console.WriteLine($"After: {context.Response.StatusCode}");
});

// Run method (terminal middleware)
app.Run(async context =>
{
    await context.Response.WriteAsync("Hello World!");
});

// Map method (branch pipeline)
app.Map("/api", apiApp =>
{
    apiApp.Use(async (context, next) =>
    {
        Console.WriteLine("API middleware");
        await next(context);
    });

    apiApp.Run(async context =>
    {
        await context.Response.WriteAsync("API Response");
    });
});

// MapWhen method (conditional branch)
app.MapWhen(
    context => context.Request.Query.ContainsKey("branch"),
    branchApp =>
    {
        branchApp.Run(async context =>
        {
            await context.Response.WriteAsync("Branch pipeline");
        });
    });

app.Run();
```

### Custom Middleware Class

```csharp
// RequestLoggingMiddleware.cs
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestL

…(truncated)
