diff --git a/visual-studio/VisualStudioVsVsCode/Directory.Build.props b/visual-studio/VisualStudioVsVsCode/Directory.Build.props
new file mode 100644
index 000000000..598633fcc
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+ $(MSBuildProjectDirectory.Substring(0, 2))
+ $(ProjectDrive.ToLowerInvariant())=$(ProjectDrive.ToUpperInvariant())
+
+
diff --git a/visual-studio/VisualStudioVsVsCode/README.md b/visual-studio/VisualStudioVsVsCode/README.md
new file mode 100644
index 000000000..9f5309e79
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/README.md
@@ -0,0 +1,38 @@
+# Visual Studio vs VS Code for .NET Developers
+
+The sample for the Code Maze article "Visual Studio vs VS Code for .NET Developers". It holds one
+minimal ASP.NET Core API, `ReportApi`, with a slow endpoint (`/report/slow`, string concatenation in
+a loop) and a fast one (`/report/fast`, `StringBuilder`), and an xUnit test project,
+`ReportApi.Tests`, with two tests.
+
+## Open in Visual Studio
+
+1. Open `VisualStudioVsVsCode.sln`.
+2. Run `ReportApi`.
+3. Browse to `http://localhost:5234/report/slow` and `http://localhost:5234/report/fast`.
+
+## Open in VS Code
+
+1. Install the C# Dev Kit extension.
+2. To use Hot Reload while debugging, run "Preferences: Open User Settings (JSON)" from the Command
+ Palette and add this entry. The setting is machine-scoped, so a workspace
+ `.vscode/settings.json` would ignore it:
+
+ ```json
+ "csharp.experimental.debug.hotReload": true
+ ```
+
+ On Windows, `Directory.Build.props` in this folder sets the compiler's `PathMap` so the debug
+ symbols store the source paths with an upper-case drive letter. In our test, the C# Dev Kit's
+ build wrote a lower-case one (`d:\`), and Hot Reload then reported "No code changes were found."
+ for every edit. The file does nothing on Linux and macOS.
+
+3. Open this folder, then run and debug `ReportApi`.
+4. With the API running, use the .NET diagnostic tools from the terminal while you call
+ `/report/slow`:
+
+ ```
+ dnx dotnet-counters monitor -n ReportApi
+ dnx dotnet-trace collect -n ReportApi --duration 00:00:00:15 -o slow.nettrace
+ dnx dotnet-trace report slow.nettrace topN -n 10
+ ```
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportApi.Tests.csproj b/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportApi.Tests.csproj
new file mode 100644
index 000000000..e0bbd7209
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportApi.Tests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportBuilderTests.cs b/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportBuilderTests.cs
new file mode 100644
index 000000000..1b6592ed7
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi.Tests/ReportBuilderTests.cs
@@ -0,0 +1,24 @@
+namespace ReportApi.Tests;
+
+public class ReportBuilderTests
+{
+ [Fact]
+ public void WhenBuiltBothWays_ThenReportsAreEqual()
+ {
+ var concatenated = ReportBuilder.BuildWithConcatenation(100);
+ var built = ReportBuilder.BuildWithStringBuilder(100);
+
+ Assert.Equal(concatenated, built);
+ }
+
+ [Fact]
+ public void WhenBuilt_ThenReportHasOneLinePerOrder()
+ {
+ var report = ReportBuilder.BuildWithStringBuilder(100);
+
+ var lines = report.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
+
+ Assert.Equal(100, lines.Length);
+ Assert.Equal("Order 1: 2 items", lines[0]);
+ }
+}
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/Program.cs b/visual-studio/VisualStudioVsVsCode/ReportApi/Program.cs
new file mode 100644
index 000000000..69ec3d13e
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/Program.cs
@@ -0,0 +1,9 @@
+using ReportApi;
+
+var builder = WebApplication.CreateBuilder(args);
+var app = builder.Build();
+
+app.MapGet("/report/slow", (int orders = 10_000) => ReportBuilder.BuildWithConcatenation(orders));
+app.MapGet("/report/fast", (int orders = 10_000) => ReportBuilder.BuildWithStringBuilder(orders));
+
+app.Run();
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/Properties/launchSettings.json b/visual-studio/VisualStudioVsVsCode/ReportApi/Properties/launchSettings.json
new file mode 100644
index 000000000..24002ad65
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/Properties/launchSettings.json
@@ -0,0 +1,23 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5234",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7032;http://localhost:5234",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/ReportApi.csproj b/visual-studio/VisualStudioVsVsCode/ReportApi/ReportApi.csproj
new file mode 100644
index 000000000..a3a34b647
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/ReportApi.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/ReportBuilder.cs b/visual-studio/VisualStudioVsVsCode/ReportApi/ReportBuilder.cs
new file mode 100644
index 000000000..c346e045b
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/ReportBuilder.cs
@@ -0,0 +1,28 @@
+using System.Text;
+
+namespace ReportApi;
+
+public static class ReportBuilder
+{
+ public static string BuildWithConcatenation(int orders)
+ {
+ var report = "";
+ for (var i = 1; i <= orders; i++)
+ {
+ report += $"Order {i}: {i % 7 + 1} items{Environment.NewLine}";
+ }
+
+ return report;
+ }
+
+ public static string BuildWithStringBuilder(int orders)
+ {
+ var report = new StringBuilder();
+ for (var i = 1; i <= orders; i++)
+ {
+ report.AppendLine($"Order {i}: {i % 7 + 1} items");
+ }
+
+ return report.ToString();
+ }
+}
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.Development.json b/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.Development.json
new file mode 100644
index 000000000..0c208ae91
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.json b/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.json
new file mode 100644
index 000000000..10f68b8c8
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/ReportApi/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/visual-studio/VisualStudioVsVsCode/VisualStudioVsVsCode.sln b/visual-studio/VisualStudioVsVsCode/VisualStudioVsVsCode.sln
new file mode 100644
index 000000000..4704214ca
--- /dev/null
+++ b/visual-studio/VisualStudioVsVsCode/VisualStudioVsVsCode.sln
@@ -0,0 +1,48 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportApi", "ReportApi\ReportApi.csproj", "{D330E484-5658-4636-A24B-EF6E0BC35EC6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportApi.Tests", "ReportApi.Tests\ReportApi.Tests.csproj", "{9B6B5296-581D-491A-95ED-398AA80CE457}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|x64.Build.0 = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Debug|x86.Build.0 = Debug|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|x64.ActiveCfg = Release|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|x64.Build.0 = Release|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|x86.ActiveCfg = Release|Any CPU
+ {D330E484-5658-4636-A24B-EF6E0BC35EC6}.Release|x86.Build.0 = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|x64.Build.0 = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Debug|x86.Build.0 = Debug|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|x64.ActiveCfg = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|x64.Build.0 = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|x86.ActiveCfg = Release|Any CPU
+ {9B6B5296-581D-491A-95ED-398AA80CE457}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal