debug:dotnet

ASP.NET Core Debugging Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "debug:dotnet" with this command: npx skills add snakeo/claude-debug-and-refactor-skills-plugin/snakeo-claude-debug-and-refactor-skills-plugin-debug-dotnet

ASP.NET Core Debugging Guide

This guide provides a systematic approach to debugging ASP.NET Core applications, covering common error patterns, diagnostic tools, and resolution strategies.

Common Error Patterns

  1. Dependency Injection (DI) Container Errors

Symptoms:

  • InvalidOperationException: Unable to resolve service for type 'X'

  • InvalidOperationException: A circular dependency was detected

  • ObjectDisposedException: Cannot access a disposed object

Common Causes:

// Missing service registration public class MyController { public MyController(IMyService service) { } // Not registered in DI }

// Circular dependency public class ServiceA { public ServiceA(ServiceB b) { } } public class ServiceB { public ServiceB(ServiceA a) { } // Circular! }

// Captive dependency (singleton holding scoped) services.AddSingleton<ISingletonService, SingletonService>(); services.AddScoped<IScopedService, ScopedService>(); // Captured by singleton!

Debugging Steps:

  • Check Program.cs or Startup.cs for service registration

  • Verify service lifetime compatibility (Singleton > Scoped > Transient)

  • Use IServiceProvider.GetRequiredService<T>() for explicit resolution

  • Enable detailed DI errors in development:

builder.Host.UseDefaultServiceProvider(options => { options.ValidateScopes = true; options.ValidateOnBuild = true; });

Resolution Patterns:

// Register missing service builder.Services.AddScoped<IMyService, MyService>();

// Break circular dependency with Lazy<T> or factory builder.Services.AddScoped<ServiceA>(); builder.Services.AddScoped<ServiceB>(sp => new ServiceB(() => sp.GetRequiredService<ServiceA>()));

// Fix captive dependency - make both singleton or use factory builder.Services.AddSingleton<IScopedService>(sp => sp.CreateScope().ServiceProvider.GetRequiredService<ScopedService>());

  1. Middleware Pipeline Issues

Symptoms:

  • Requests returning unexpected status codes

  • Authentication/authorization not working

  • CORS errors

  • Request body already read exceptions

  • Response already started exceptions

Common Causes:

// Wrong middleware order app.UseAuthorization(); // Must come AFTER UseAuthentication! app.UseAuthentication();

// Missing middleware app.UseRouting(); // Missing UseAuthentication() and UseAuthorization() app.MapControllers();

// Response already started app.Use(async (context, next) => { await context.Response.WriteAsync("Hello"); // Response started await next(); // Next middleware tries to modify headers - ERROR });

Debugging Steps:

  • Add diagnostic middleware to trace pipeline:

app.Use(async (context, next) => { Console.WriteLine($"Request: {context.Request.Path}"); await next(); Console.WriteLine($"Response: {context.Response.StatusCode}"); });

  • Check middleware order matches recommended sequence

  • Enable developer exception page in development

  • Check for Response.HasStarted before modifying response

Correct Middleware Order:

app.UseExceptionHandler("/error"); app.UseHsts(); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); app.UseSession(); app.MapControllers();

  1. Entity Framework Core Query Problems

Symptoms:

  • InvalidOperationException: The instance of entity type 'X' cannot be tracked

  • N+1 query problems (excessive database queries)

  • DbUpdateConcurrencyException

  • Lazy loading returning null

  • Query performance issues

Common Causes:

// N+1 problem var orders = context.Orders.ToList(); foreach (var order in orders) { Console.WriteLine(order.Customer.Name); // Each access = new query }

// Tracking conflict var entity = context.Products.Find(1); context.Products.Update(new Product { Id = 1 }); // Already tracked!

// Missing Include for related data var order = context.Orders.First(); // Customer is null!

Debugging Steps:

  • Enable sensitive data logging:

optionsBuilder .EnableSensitiveDataLogging() .EnableDetailedErrors() .LogTo(Console.WriteLine, LogLevel.Information);

  • Use SQL Server Profiler or EF Core logging to inspect queries

  • Check for AsNoTracking() usage on read-only queries

  • Verify Include() statements for related entities

Resolution Patterns:

// Eager loading to prevent N+1 var orders = context.Orders .Include(o => o.Customer) .Include(o => o.OrderItems) .ToList();

// Use AsNoTracking for read-only queries var products = context.Products .AsNoTracking() .Where(p => p.Price > 100) .ToList();

// Handle concurrency with retry try { await context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { var entry = ex.Entries.Single(); await entry.ReloadAsync(); // Retry or merge changes }

// Split queries for complex includes var orders = context.Orders .Include(o => o.OrderItems) .AsSplitQuery() .ToList();

  1. Configuration Binding Failures

Symptoms:

  • Configuration values are null or default

  • InvalidOperationException when binding to options

  • Environment-specific settings not loading

  • Secrets not being read

Common Causes:

// Mismatched property names public class MyOptions { public string ConnectionString { get; set; } // But appsettings has "connectionString" }

// Missing section services.Configure<MyOptions>(config.GetSection("NonExistent"));

// Wrong environment file // appsettings.Development.json not loading in Development

Debugging Steps:

  • Log all configuration sources:

foreach (var source in builder.Configuration.Sources) { Console.WriteLine(source.GetType().Name); }

  • Dump effective configuration:

Console.WriteLine(builder.Configuration.GetDebugView());

  • Verify ASPNETCORE_ENVIRONMENT is set correctly

  • Check file names match exactly (case-sensitive on Linux)

Resolution Patterns:

// Validate options on startup services.AddOptions<MyOptions>() .Bind(config.GetSection("MyOptions")) .ValidateDataAnnotations() .ValidateOnStart();

// Use strongly-typed configuration public class MyOptions { public const string SectionName = "MySettings";

[Required]
public string ApiKey { get; set; } = string.Empty;

[Range(1, 100)]
public int MaxRetries { get; set; } = 3;

}

// Load with validation builder.Services.AddOptions<MyOptions>() .BindConfiguration(MyOptions.SectionName) .ValidateDataAnnotations() .ValidateOnStart();

  1. Authentication/Authorization Issues

Symptoms:

  • 401 Unauthorized when credentials are correct

  • 403 Forbidden with correct roles

  • JWT token validation failures

  • Cookie authentication not persisting

  • Claims not available in controllers

Common Causes:

// Missing authentication scheme [Authorize] // No scheme specified, uses default public class MyController { }

// Wrong claim type for roles options.TokenValidationParameters = new TokenValidationParameters { RoleClaimType = "role", // But token has "roles" };

// Cookie not being sent (SameSite issues) options.Cookie.SameSite = SameSiteMode.Strict; // Blocks cross-site

Debugging Steps:

  • Log authentication events:

services.AddAuthentication() .AddJwtBearer(options => { options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { Console.WriteLine($"Auth failed: {context.Exception}"); return Task.CompletedTask; }, OnTokenValidated = context => { Console.WriteLine($"Token validated for: {context.Principal?.Identity?.Name}"); return Task.CompletedTask; } }; });

  • Inspect JWT tokens at jwt.io

  • Check claims in controller: User.Claims.Select(c => $"{c.Type}: {c.Value}")

  • Verify HTTPS is configured correctly for secure cookies

Resolution Patterns:

// Configure JWT properly services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = "your-issuer", ValidateAudience = true, ValidAudience = "your-audience", ValidateLifetime = true, ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes("your-secret-key-at-least-32-chars")), RoleClaimType = ClaimTypes.Role, NameClaimType = ClaimTypes.Name }; });

// Configure CORS for cookie auth services.AddCors(options => { options.AddPolicy("AllowFrontend", builder => builder.WithOrigins("https://frontend.com") .AllowCredentials() .AllowAnyHeader() .AllowAnyMethod()); });

  1. Startup and Hosting Failures

Symptoms:

  • 502.5 Process Failure on IIS/Azure

  • Application fails to start with no clear error

  • HostAbortedException during startup

  • Port already in use errors

Common Causes:

// Missing required service var myService = app.Services.GetRequiredService<IMyService>(); // Throws on startup

// Database not available during startup await context.Database.MigrateAsync(); // DB not ready

// Incorrect program entry public static void Main(string[] args) { } // Missing async/await

Debugging Steps:

  • Enable stdout logging for IIS:

<!-- web.config --> <aspNetCore processPath="dotnet" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />

  • Check Windows Event Viewer for .NET Runtime errors

  • Run application directly from command line:

dotnet MyApp.dll --urls "http://localhost:5000"

  • Use ASPNETCORE_DETAILEDERRORS=true environment variable

Resolution Patterns:

// Graceful startup with error handling try { var builder = WebApplication.CreateBuilder(args); // Configure services var app = builder.Build(); // Configure pipeline await app.RunAsync(); } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); } finally { Log.CloseAndFlush(); }

// Handle database not ready services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(connectionString, sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); }); });

Debugging Tools

Visual Studio Debugger

Essential Features:

  • Breakpoints: Conditional, hit count, filter by thread

  • Exception Settings: Break on specific exceptions (Debug > Windows > Exception Settings)

  • Immediate Window: Evaluate expressions during debugging

  • Diagnostic Tools: CPU, memory, events timeline

  • Hot Reload: Edit code during debugging (supported operations)

Advanced Techniques:

// Conditional breakpoint expression User.IsAuthenticated && Request.Path.StartsWithSegments("/api")

// Tracepoint (log without stopping) // Right-click breakpoint > Actions > Log a message "Request to {Request.Path} at {DateTime.Now}"

// DebuggerDisplay attribute for better visualization [DebuggerDisplay("Order {Id}: {Customer.Name} - ${Total}")] public class Order { }

Visual Studio Code Debugging

launch.json Configuration:

{ "version": "0.2.0", "configurations": [ { "name": ".NET Core Launch (web)", "type": "coreclr", "request": "launch", "preLaunchTask": "build", "program": "${workspaceFolder}/bin/Debug/net8.0/MyApp.dll", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, "env": { "ASPNETCORE_ENVIRONMENT": "Development", "ASPNETCORE_URLS": "https://localhost:5001" } }, { "name": ".NET Core Attach", "type": "coreclr", "request": "attach", "processId": "${command:pickProcess}" } ] }

dotnet-trace

Performance tracing and diagnostics:

Install

dotnet tool install --global dotnet-trace

Collect trace from running process

dotnet-trace collect --process-id <PID>

Collect with specific providers

dotnet-trace collect -p <PID> --providers Microsoft-Extensions-Logging

Collect CPU profile

dotnet-trace collect -p <PID> --profile cpu-sampling

Convert to speedscope format for visualization

dotnet-trace convert trace.nettrace --format speedscope

dotnet-dump

Memory dump analysis:

Install

dotnet tool install --global dotnet-dump

Collect dump from running process

dotnet-dump collect -p <PID>

Analyze dump

dotnet-dump analyze dump.dmp

Common SOS commands in analyze mode

dumpheap -stat # Heap statistics dumpheap -type MyClass # Find specific type instances gcroot <address> # Find GC roots for object threadpool # Thread pool info eestack # Managed stack traces

dotnet-counters

Real-time performance monitoring:

Install

dotnet tool install --global dotnet-counters

Monitor default counters

dotnet-counters monitor -p <PID>

Monitor specific counters

dotnet-counters monitor -p <PID> --counters
System.Runtime,
Microsoft.AspNetCore.Hosting,
Microsoft-AspNetCore-Server-Kestrel

Export to CSV

dotnet-counters collect -p <PID> --format csv -o counters.csv

ILogger and Logging Frameworks

Built-in Logging Configuration:

// Program.cs builder.Logging .ClearProviders() .AddConsole() .AddDebug() .SetMinimumLevel(LogLevel.Debug);

// Filter by category builder.Logging.AddFilter("Microsoft.EntityFrameworkCore", LogLevel.Warning); builder.Logging.AddFilter("MyApp", LogLevel.Debug);

Structured Logging with Serilog:

// Install: Serilog.AspNetCore Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7) .WriteTo.Seq("http://localhost:5341") // Centralized logging .CreateLogger();

builder.Host.UseSerilog();

Logging Best Practices:

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

public async Task&#x3C;Order> ProcessOrderAsync(int orderId)
{
    // Use structured logging with meaningful context
    using (_logger.BeginScope(new Dictionary&#x3C;string, object>
    {
        ["OrderId"] = orderId,
        ["CorrelationId"] = Activity.Current?.Id
    }))
    {
        _logger.LogInformation("Processing order {OrderId}", orderId);

        try
        {
            var order = await GetOrderAsync(orderId);
            _logger.LogDebug("Order details: {@Order}", order);
            return order;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to process order {OrderId}", orderId);
            throw;
        }
    }
}

}

Application Insights

Setup and Configuration:

// Install: Microsoft.ApplicationInsights.AspNetCore builder.Services.AddApplicationInsightsTelemetry();

// Configure in appsettings.json { "ApplicationInsights": { "ConnectionString": "InstrumentationKey=xxx;IngestionEndpoint=xxx" } }

Custom Telemetry:

public class PaymentService { private readonly TelemetryClient _telemetry;

public async Task ProcessPaymentAsync(Payment payment)
{
    using var operation = _telemetry.StartOperation&#x3C;RequestTelemetry>("ProcessPayment");

    _telemetry.TrackEvent("PaymentStarted", new Dictionary&#x3C;string, string>
    {
        ["PaymentId"] = payment.Id.ToString(),
        ["Amount"] = payment.Amount.ToString("C")
    });

    try
    {
        // Process payment
        _telemetry.TrackMetric("PaymentAmount", (double)payment.Amount);
    }
    catch (Exception ex)
    {
        _telemetry.TrackException(ex);
        operation.Telemetry.Success = false;
        throw;
    }
}

}

The Four Phases of ASP.NET Core Debugging

Phase 1: Reproduce and Isolate

Goals: Consistently reproduce the issue and narrow down the scope.

Actions:

  • Capture exact steps to reproduce

  • Note environment details (OS, .NET version, configuration)

  • Check if issue occurs in Development vs Production

  • Isolate to specific endpoint, service, or component

Commands:

Check .NET version and environment

dotnet --info

Verify configuration

dotnet run --environment Development

Test specific endpoint

curl -v https://localhost:5001/api/health

Check if issue is environment-specific

ASPNETCORE_ENVIRONMENT=Development dotnet run ASPNETCORE_ENVIRONMENT=Production dotnet run

Phase 2: Gather Evidence

Goals: Collect logs, traces, and diagnostic data.

Actions:

  • Enable verbose logging

  • Capture request/response details

  • Monitor performance counters

  • Collect memory dumps if needed

Configuration for Maximum Diagnostics:

// appsettings.Development.json { "Logging": { "LogLevel": { "Default": "Debug", "Microsoft.AspNetCore": "Debug", "Microsoft.EntityFrameworkCore": "Debug", "Microsoft.EntityFrameworkCore.Database.Command": "Information" } } }

Middleware for Request Logging:

app.Use(async (context, next) => { var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();

logger.LogDebug("Request: {Method} {Path} {Query}",
    context.Request.Method,
    context.Request.Path,
    context.Request.QueryString);

// Capture request body (careful with large bodies)
context.Request.EnableBuffering();

var stopwatch = Stopwatch.StartNew();
await next();
stopwatch.Stop();

logger.LogDebug("Response: {StatusCode} in {ElapsedMs}ms",
    context.Response.StatusCode,
    stopwatch.ElapsedMilliseconds);

});

Phase 3: Analyze and Hypothesize

Goals: Form theories based on evidence and test them.

Common Analysis Patterns:

For DI Errors:

// List all registered services foreach (var service in builder.Services) { Console.WriteLine($"{service.ServiceType.Name} -> {service.ImplementationType?.Name} ({service.Lifetime})"); }

For EF Core Issues:

// Log generated SQL optionsBuilder.LogTo( message => Debug.WriteLine(message), new[] { DbLoggerCategory.Database.Command.Name }, LogLevel.Information);

For Authentication Issues:

// Dump all claims app.Use(async (context, next) => { if (context.User.Identity?.IsAuthenticated == true) { foreach (var claim in context.User.Claims) { Console.WriteLine($"Claim: {claim.Type} = {claim.Value}"); } } await next(); });

Phase 4: Fix and Verify

Goals: Implement fix with confidence and prevent regression.

Best Practices:

  • Write a failing test that reproduces the bug

  • Implement the minimal fix

  • Verify the test passes

  • Check for side effects

  • Add logging/monitoring for the fixed code path

Example Test-Driven Fix:

[Fact] public async Task ProcessOrder_WhenCustomerNotFound_ThrowsNotFoundException() { // Arrange - This test should fail before fix var mockRepo = new Mock<ICustomerRepository>(); mockRepo.Setup(r => r.GetByIdAsync(It.IsAny<int>())) .ReturnsAsync((Customer)null);

var service = new OrderService(mockRepo.Object);

// Act &#x26; Assert
await Assert.ThrowsAsync&#x3C;CustomerNotFoundException>(
    () => service.ProcessOrderAsync(1, customerId: 999));

}

Quick Reference Commands

Build and Run

Build project

dotnet build

Build in Release mode

dotnet build -c Release

Run with hot reload

dotnet watch run

Run specific project

dotnet run --project src/MyApp/MyApp.csproj

Run with specific environment

ASPNETCORE_ENVIRONMENT=Development dotnet run

Publish for deployment

dotnet publish -c Release -o ./publish

Entity Framework Core

List migrations

dotnet ef migrations list

Add new migration

dotnet ef migrations add AddUserTable

Update database

dotnet ef database update

Generate SQL script

dotnet ef migrations script --idempotent

Remove last migration

dotnet ef migrations remove

Drop database

dotnet ef database drop --force

Scaffold from existing database

dotnet ef dbcontext scaffold "Server=.;Database=MyDb;Trusted_Connection=True" Microsoft.EntityFrameworkCore.SqlServer

Testing

Run all tests

dotnet test

Run with verbosity

dotnet test -v detailed

Run specific test

dotnet test --filter "FullyQualifiedName~OrderServiceTests"

Run with coverage

dotnet test --collect:"XPlat Code Coverage"

Generate coverage report

reportgenerator -reports:coverage.cobertura.xml -targetdir:coveragereport

Diagnostics

List running .NET processes

dotnet-trace ps

Collect trace

dotnet-trace collect -p <PID> --duration 00:00:30

Monitor counters

dotnet-counters monitor -p <PID>

Collect memory dump

dotnet-dump collect -p <PID>

Analyze dump

dotnet-dump analyze core_dump.dmp

GC dump

dotnet-gcdump collect -p <PID>

Package Management

Add package

dotnet add package Serilog.AspNetCore

Remove package

dotnet remove package OldPackage

List packages

dotnet list package

List outdated packages

dotnet list package --outdated

Restore packages

dotnet restore

Clear NuGet cache

dotnet nuget locals all --clear

Secrets Management

Initialize user secrets

dotnet user-secrets init

Set a secret

dotnet user-secrets set "ConnectionStrings:Default" "Server=..."

List secrets

dotnet user-secrets list

Remove a secret

dotnet user-secrets remove "ConnectionStrings:Default"

Clear all secrets

dotnet user-secrets clear

Troubleshooting Checklist

Before Debugging

  • Reproduce the issue consistently

  • Check if issue occurs in Development mode

  • Verify .NET SDK/runtime version matches

  • Confirm database is accessible

  • Check environment variables are set

During Debugging

  • Enable detailed error pages (app.UseDeveloperExceptionPage() )

  • Set logging to Debug level

  • Check for null reference exceptions

  • Verify DI service registrations

  • Inspect middleware pipeline order

  • Check EF Core generated SQL

  • Verify authentication claims

After Fixing

  • Write test to prevent regression

  • Update logging for the fixed code path

  • Document the issue and solution

  • Check for similar issues elsewhere

  • Consider adding health checks

Resources

  • ASP.NET Core Documentation

  • Entity Framework Core Documentation

  • .NET Diagnostics Documentation

  • Serilog Documentation

  • Application Insights Documentation

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

refactor:flutter

No summary provided by upstream source.

Repository SourceNeeds Review
General

refactor:nestjs

No summary provided by upstream source.

Repository SourceNeeds Review
General

debug:flutter

No summary provided by upstream source.

Repository SourceNeeds Review
General

refactor:spring-boot

No summary provided by upstream source.

Repository SourceNeeds Review