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