Visual Studio Code (VSCode) is favored by developers for its lightweight footprint, rich extension ecosystem, and powerful capabilities. When it comes to .NET development, VSCode can be fine-tuned to deliver a robust debugging experience on par with full-featured IDEs like Visual Studio. In this comprehensive guide, we’ll walk you through every step of setting up VSCode for .NET debugging-from installing essential extensions and configuring launch settings to harnessing advanced debugging features.
Table of Contents
- Prerequisites
- Installing Required Extensions
- Understanding the .vscode Folder
- Configuring launch.json
- Configuring tasks.json
- Debugging Features
- Advanced Debugging Techniques
- Troubleshooting
Prerequisites
Before setting up VSCode for .NET debugging, ensure you have:
.NET SDK Installation
Download and install the .NET SDK from https://dotnet.microsoft.com/download
Verify installation:
dotnet --version
You should see output like:
10.0.100
Check installed SDKs:
dotnet --list-sdks
Installing Required Extensions
C# Dev Kit (Primary Extension)
The C# Dev Kit is the official Microsoft extension for .NET development in VSCode.
Installation Steps:
- Open VSCode
- Access Extensions (Ctrl+Shift+X on Windows/Linux, Cmd+Shift+X on macOS)
- Search for “C# Dev Kit”
- Install the extension by Microsoft
The C# Dev Kit automatically installs these companion extensions:
- C# - Base language support
- IntelliCode for C# Dev Kit - AI-assisted development
- .NET Install Tool - Manages .NET SDK installations
What C# Dev Kit Provides:
- IntelliSense (code completion)
- Code navigation (Go to Definition, Find References)
- Debugging support
- Solution Explorer
- Test Explorer
- Project templates
- Refactoring tools
Verifying Extension Installation
- Open Command Palette (Ctrl+Shift+P / Cmd+Shift+P)
- Type “Developer: Show Running Extensions”
- Verify these extensions are active:
- C# Dev Kit
- C#
- C# IntelliCode
Understanding the .vscode Folder
When you configure a .NET project for debugging, VSCode creates a .vscode folder in your workspace root with three key files:
your-project/
├── .vscode/
│ ├── launch.json # Debug configurations
│ ├── tasks.json # Build and task automation
│ └── settings.json # Workspace-specific settings
├── Program.cs
├── YourProject.csproj
└── YourSolution.sln
File Purposes:
- launch.json: Defines how to launch and debug your application
- tasks.json: Defines build, test, and other automation tasks
- settings.json: Workspace-specific editor settings
Configuring launch.json
The launch.json file is the heart of your debugging configuration. Let’s build one from scratch.
Auto-Generating launch.json
The easiest way to create a launch.json:
- Open your .NET project in VSCode
- Go to Run and Debug view (Ctrl+Shift+D / Cmd+Shift+D)
- Click “create a launch.json file”
- Select ”.NET 5+ and .NET Core” from the environment list
VSCode will generate a basic configuration.
Understanding launch.json Structure
Here’s a detailed breakdown:
{
"version": "0.2.0",
"configurations": [
{
// Human-readable name shown in debug dropdown
"name": ".NET Core Launch (console)",
// Debugger type: "coreclr" for .NET Core/5+
"type": "coreclr",
// "launch" = start new process
// "attach" = attach to running process
"request": "launch",
// Task to run before debugging
"preLaunchTask": "build",
// Path to compiled .dll
"program": "${workspaceFolder}/bin/Debug/net10.0/YourApp.dll",
// Command line arguments
"args": [],
// Working directory when app runs
"cwd": "${workspaceFolder}",
// Console type: "integratedTerminal", "internalConsole", "externalTerminal"
"console": "integratedTerminal",
// Whether to break at entry point
"stopAtEntry": false
}
]
}
Variable Substitution
VSCode supports these variables in launch.json:
${workspaceFolder}- Root folder opened in VSCode${workspaceFolderBasename}- Folder name without slashes${file}- Currently opened file${fileBasename}- Currently opened filename${fileDirname}- Directory of currently opened file${fileExtname}- Extension of current file${cwd}- Current working directory${env:VARIABLE}- Environment variable
Real-World Example: Multiple Launch Configurations
Here’s a practical example showing multiple configurations for a CLI application:
{
"version": "0.2.0",
"configurations": [
{
"name": "CLI: Import Data",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/MyCliApp.dll",
"args": ["import", "--source", "data.csv", "--batch-size", "100"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false,
"env": {
"DATABASE_CONNECTION": "Server=localhost;Database=mydb;",
"LOG_LEVEL": "Debug"
}
},
{
"name": "CLI: Export Data",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/MyCliApp.dll",
"args": ["export", "--format", "json", "--output", "export.json"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false,
"env": {
"DATABASE_CONNECTION": "Server=localhost;Database=mydb;"
}
},
{
"name": "CLI: Process Queue",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/MyCliApp.dll",
"args": ["process", "--workers", "5", "--timeout", "30"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false,
"env": {
"QUEUE_CONNECTION": "Endpoint=sb://...",
"ENVIRONMENT": "Development"
}
},
{
"name": "CLI: Custom Arguments",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/MyCliApp.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false,
"env": {
"DATABASE_CONNECTION": "Server=localhost;Database=mydb;"
}
}
]
}
Key Configuration Properties Explained
1. Console Types
"console": "integratedTerminal" // VSCode's integrated terminal (recommended)
"console": "internalConsole" // Debug Console panel (no input support)
"console": "externalTerminal" // System terminal window
When to use each:
- integratedTerminal: Best for most scenarios, allows user input
- internalConsole: Read-only output, good for simple logging
- externalTerminal: When you need native terminal features
2. Environment Variables
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"SERVICEBUS_CONNECTION_STRING": "Endpoint=sb://...",
"LOG_LEVEL": "Debug"
}
3. Source File Map (for debugging published apps)
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
4. Just My Code
"justMyCode": false // Enable to step into framework code
5. Require Exact Source
"requireExactSource": false // Allow debugging when source doesn't match
Configuration for Different Project Types
Console Application
{
"name": ".NET Console App",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/ConsoleApp.dll",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false
}
ASP.NET Core Web API
{
"name": ".NET Web API",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0/WebApi.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:5001;http://localhost:5000"
},
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
}
}
The serverReadyAction automatically opens your browser when the web server is ready.
Blazor WebAssembly
{
"name": "Blazor WASM",
"type": "blazorwasm",
"request": "launch",
"cwd": "${workspaceFolder}",
"browser": "edge"
}
Attach to Running Process
{
"name": "Attach to Process",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
When you debug with this configuration, VSCode shows a process picker.
Configuring tasks.json
The tasks.json file defines automated tasks like building, testing, and publishing.
Auto-Generating tasks.json
- Press Ctrl+Shift+P (Cmd+Shift+P on macOS)
- Type “Tasks: Configure Task”
- Select “Create tasks.json from template”
- Choose ”.NET Core”
Understanding tasks.json Structure
{
"version": "2.0.0",
"tasks": [
{
// Human-readable task name
"label": "build",
// Command to execute
"command": "dotnet",
// Process type (vs "shell")
"type": "process",
// Command arguments
"args": [
"build",
"${workspaceFolder}/YourProject.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
// How to detect errors in output
"problemMatcher": "$msCompile",
// Task grouping
"group": {
"kind": "build",
"isDefault": true // This is the default build task
}
}
]
}
Complete tasks.json Example
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/YourProject.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/YourProject.csproj",
"-c", "Release",
"-o", "${workspaceFolder}/publish",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/YourProject.csproj"
],
"problemMatcher": "$msCompile",
"isBackground": true
},
{
"label": "clean",
"command": "dotnet",
"type": "process",
"args": [
"clean",
"${workspaceFolder}/YourProject.csproj"
],
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",
"type": "process",
"args": [
"test",
"${workspaceFolder}/YourProject.Tests.csproj"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "test",
"isDefault": true
}
}
]
}
Task Properties Explained
Problem Matchers
Problem matchers parse task output to detect errors and warnings:
$msCompile- MSBuild/C# compiler errors$tsc- TypeScript compiler$eslint-compact- ESLint errors
Custom problem matcher:
"problemMatcher": {
"owner": "custom",
"fileLocation": ["relative", "${workspaceFolder}"],
"pattern": {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
}
Task Dependencies
Run multiple tasks in sequence:
{
"label": "build-and-test",
"dependsOn": ["clean", "build", "test"],
"dependsOrder": "sequence"
}
Background Tasks
For long-running processes like dotnet watch:
{
"label": "watch",
"isBackground": true,
"problemMatcher": {
"base": "$msCompile",
"background": {
"activeOnStart": true,
"beginsPattern": "^\\s*Waiting for a file to change",
"endsPattern": "^\\s*Application started"
}
}
}
Debugging Features
Setting Breakpoints
Standard Breakpoints
- Click in the gutter (left of line numbers)
- Or press F9 on the line
- Red dot appears
Conditional Breakpoints
Right-click breakpoint > Edit Breakpoint
Expression condition:
userId == "admin"
Hit count:
>= 10
Log message (tracepoint):
User {userId} processed at {DateTime.Now}
Debug Actions
| Action | Shortcut | Description |
|---|---|---|
| Continue | F5 | Resume execution |
| Step Over | F10 | Execute current line, skip function internals |
| Step Into | F11 | Enter function call |
| Step Out | Shift+F11 | Exit current function |
| Restart | Ctrl+Shift+F5 | Restart debugging session |
| Stop | Shift+F5 | Stop debugging |
Debug Views
Variables View
Shows all local variables, arguments, and their current values.
Watch custom expressions:
- Hover over variable
- Click + to add to Watch
Watch View
Add expressions to monitor:
message.Length
DateTime.Now.ToString()
user?.Email ?? "No email"
Call Stack View
Shows execution path:
Main() Program.cs:15
ProcessMessage() MessageHandler.cs:42
ValidateMessage() MessageValidator.cs:28
Click any frame to see local context.
Breakpoints View
Lists all breakpoints. Check/uncheck to enable/disable without removing.
Debug Console
Evaluate expressions during debugging:
> message.Properties.Count
5
> DateTime.UtcNow
2025-12-11 10:30:00
> user.Email
"admin@example.com"
Execute methods:
> Console.WriteLine("Debug message")
Debug message
Advanced Debugging Techniques
1. Data Breakpoints (Memory Breakpoints)
Break when a variable’s value changes:
In Variables view:
- Right-click variable
- Select “Break on Value Change”
2. Exception Settings
Configure which exceptions break execution:
- Open Run and Debug view
- Click “Exception Settings” in toolbar
- Check/uncheck exception types
Or in launch.json:
"exceptionOptions": {
"breakOnAll": false,
"breakOnAny": [
"System.NullReferenceException",
"System.ArgumentException"
]
}
3. Edit and Continue
Modify code while debugging (limited support in .NET):
- Pause execution at breakpoint
- Edit code
- Continue - changes apply if possible
Enable in launch.json:
"enableStepFiltering": false,
"enableEditAndContinue": true
4. Source Link
Debug NuGet packages with original source code:
"justMyCode": false,
"requireExactSource": false,
"symbolOptions": {
"searchPaths": [],
"searchMicrosoftSymbolServer": true,
"searchNuGetOrgSymbolServer": true
}
5. Multi-Target Debugging
Debug multiple processes simultaneously:
{
"version": "0.2.0",
"configurations": [
{
"name": "API",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/api/bin/Debug/net10.0/api.dll"
},
{
"name": "Worker",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/worker/bin/Debug/net10.0/worker.dll"
}
],
"compounds": [
{
"name": "API + Worker",
"configurations": ["API", "Worker"],
"stopAll": true
}
]
}
6. Remote Debugging
Debug application running on another machine:
On remote machine:
# Install vsdbg
curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg
launch.json:
{
"name": "Remote Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickRemoteProcess}",
"pipeTransport": {
"pipeCwd": "${workspaceFolder}",
"pipeProgram": "ssh",
"pipeArgs": [
"user@remote-host"
],
"debuggerPath": "~/vsdbg/vsdbg"
}
}
7. Hot Reload
See code changes without restarting:
Start with:
dotnet watch
Or in tasks.json:
{
"label": "watch",
"command": "dotnet",
"args": ["watch", "--project", "${workspaceFolder}/YourProject.csproj"],
"isBackground": true
}
8. Debugging Tests
Create test-specific configuration:
{
"name": "Debug Tests",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"test",
"${workspaceFolder}/tests/YourProject.Tests/YourProject.Tests.csproj",
"--filter", "FullyQualifiedName~YourNamespace.YourTestClass.YourTestMethod"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}
Or use Test Explorer:
- Open Testing view (beaker icon in sidebar)
- Right-click test
- Select “Debug Test”
9. Logpoints (Non-Breaking Breakpoints)
Log without stopping execution:
- Right-click line number
- Select “Add Logpoint”
- Enter message:
Message count: {messages.Count}
10. Function Breakpoints
Break when specific function is called:
- Debug view > Breakpoints section
- Click + dropdown
- Select “Function Breakpoint”
- Enter function name:
MyNamespace.MyClass.MyMethod
Workspace Settings for .NET
Configure .vscode/settings.json for better .NET experience:
{
// C# specific
"dotnet.defaultSolution": "YourSolution.sln",
"csharp.semanticHighlighting.enabled": true,
"csharp.suppressDotnetInstallWarning": false,
// IntelliCode
"csharp.inlayHints.enableInlayHintsForParameters": true,
"csharp.inlayHints.enableInlayHintsForLiteralParameters": true,
"csharp.inlayHints.enableInlayHintsForIndexerParameters": true,
"csharp.inlayHints.enableInlayHintsForObjectCreationParameters": true,
"csharp.inlayHints.enableInlayHintsForOtherParameters": true,
"csharp.inlayHints.suppressInlayHintsForParametersThatDifferOnlyBySuffix": false,
// Code formatting
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableRoslynAnalyzers": true,
"omnisharp.organizeImportsOnFormat": true,
// Terminal
"terminal.integrated.env.windows": {
"PATH": "${env:PATH};C:\\Program Files\\dotnet"
},
// Files
"files.exclude": {
"**/bin": true,
"**/obj": true
},
// Search
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/bin": true,
"**/obj": true
}
}
Troubleshooting
Issue: “Cannot find .NET SDK”
Solution:
- Verify SDK installation:
dotnet --list-sdks - Reload window: Ctrl+Shift+P > “Developer: Reload Window”
- Set SDK path in settings:
"dotnet.dotnetPath": "/usr/local/share/dotnet/dotnet"
Issue: “Breakpoint won’t hit”
Solutions:
- Verify build configuration is Debug not Release
- Check
Debug/folder has recent build - Disable “Just My Code”:
"justMyCode": false
- Clean and rebuild:
dotnet clean
dotnet build
Issue: “The preLaunchTask ‘build’ terminated with exit code 1”
Solutions:
- Run build manually:
dotnet build - Check project path in tasks.json matches your .csproj
- Verify .NET SDK version matches project target framework
Issue: Variables show “Cannot evaluate expression”
Solutions:
- Ensure debug symbols are generated (check .csproj):
<PropertyGroup>
<DebugType>portable</DebugType>
</PropertyGroup>
- Rebuild in Debug configuration
- Disable compiler optimizations
Issue: “Debugging with browser doesn’t work”
Solution for ASP.NET Core:
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)",
"uriFormat": "%s"
}
Issue: Can’t debug NuGet packages
Solution:
"justMyCode": false,
"symbolOptions": {
"searchMicrosoftSymbolServer": true,
"searchNuGetOrgSymbolServer": true
}
Issue: “Launch.json program path is incorrect”
Solution: Verify paths match your project structure:
"program": "${workspaceFolder}/bin/Debug/net10.0/ProjectName.dll"
Check:
- Workspace folder is correct
- Project path is correct
- Target framework version matches (net10.0, net9.0, etc.)
- DLL name matches project name
Best Practices
1. Organize Configurations by Environment
{
"configurations": [
{
"name": "Development",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
{
"name": "Staging",
"env": {
"ASPNETCORE_ENVIRONMENT": "Staging"
}
}
]
}
2. Use Descriptive Configuration Names
Bad:
"name": "Launch 1"
Good:
"name": "API: Development (Port 5000)"
3. Leverage User Secrets
Don’t commit sensitive data in launch.json:
Setup:
dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:Database" "Server=..."
Access in code:
var connectionString = configuration["ConnectionStrings:Database"];
4. Use Input Variables for Flexibility
{
"version": "0.2.0",
"inputs": [
{
"id": "messageCount",
"type": "promptString",
"description": "Number of messages to process",
"default": "10"
}
],
"configurations": [
{
"name": "Process Messages",
"args": ["--count", "${input:messageCount}"]
}
]
}
5. Create Compound Configurations for Microservices
{
"compounds": [
{
"name": "Full Stack",
"configurations": [
"Frontend",
"API Gateway",
"Auth Service",
"Data Service"
],
"stopAll": true,
"preLaunchTask": "build-all"
}
]
}
Useful Keyboard Shortcuts
| Action | Windows/Linux | macOS |
|---|---|---|
| Start Debugging | F5 | F5 |
| Run Without Debugging | Ctrl+F5 | Cmd+F5 |
| Toggle Breakpoint | F9 | F9 |
| Step Over | F10 | F10 |
| Step Into | F11 | F11 |
| Step Out | Shift+F11 | Shift+F11 |
| Continue | F5 | F5 |
| Stop | Shift+F5 | Shift+F5 |
| Restart | Ctrl+Shift+F5 | Cmd+Shift+F5 |
| Show Debug Console | Ctrl+Shift+Y | Cmd+Shift+Y |
| Open Run and Debug | Ctrl+Shift+D | Cmd+Shift+D |
Additional Resources
Official Documentation
Extensions Worth Installing
- C# Dev Kit - Essential for .NET development
- NuGet Gallery - Manage NuGet packages
- REST Client - Test APIs
- GitLens - Enhanced Git integration
- Error Lens - Inline error highlighting
- Better Comments - Colorful comment annotations
Community Resources
Conclusion
Setting up VSCode for .NET debugging is straightforward once you understand the key configuration files:
- launch.json - How to run and debug
- tasks.json - Build automation
- settings.json - Workspace preferences
Start with auto-generated configurations and customize as your project grows. Use multiple configurations for different scenarios, leverage environment variables for secrets, and explore advanced features like conditional breakpoints and hot reload.
With this setup, you’ll have a professional .NET development environment that rivals Visual Studio in debugging capabilities while maintaining VSCode’s speed and flexibility.
Created: December 2025 Version: .NET 10.0 / VSCode Latest Updated: Based on C# Dev Kit latest features