Mert Özen Further With Every Line
Developer Tools IDEs — #06

Full-Stack .NET and Angular in VS Code: C# Dev Kit, Angular Language Service, Profiles and Dev Containers

Mert Özen Nov 8, 2026 9 min 4 views
Full-Stack .NET and Angular in VS Code: C# Dev Kit, Angular Language Service, Profiles and Dev Containers

For .NET and Angular in VS Code, C# Dev Kit on top of the C# extension, the Angular Language Service and .vscode settings in the repo are enough; put the environment in a Dev Container and a new developer only needs Docker and VS Code. In a real run the container brought .NET SDK 10.0.403 and Node 26.13.0, but since forwardPorts is VS Code's job, the API in a CLI-started container wasn't reachable from the host. Settings layers, profiles, Remote SSH, WSL and agent windows.

Version note: The examples in this post were run with Visual Studio Code (VS Code) 1.145.2, the .NET 10 SDK (Software Development Kit), Dev Container CLI (Command Line Interface) 0.90.0 and Docker 29.2; inside the Dev Container, .NET SDK 10.0.403 and Node.js 26.13.0 (LTS, Long-Term Support) ran. To avoid touching the user's VS Code profile, the editor UI (User Interface) was not opened for this post and no extensions were installed; the Dev Container was brought up with the official CLI, which implements the same devcontainer.json specification VS Code uses. C# Dev Kit, Angular Language Service, profile and settings behavior are described from the official documentation.

Guess First

You give a new developer on the Bookstore team two options. The first is a one-page README: "install the .NET 10 SDK, install Node's LTS release, install the Angular CLI, install these extensions". The second is the repo's .devcontainer/devcontainer.json: say "Reopen in Container" in VS Code and let everything else install itself. On the second path, how many things does the new developer have to install on their machine before the first build? And can they reach the API (Application Programming Interface) running on 5046 inside the container from their own browser?

In this post we set up VS Code as a working environment that runs .NET and Angular together: which extension for the C# side, what Angular needs, which layer settings belong in, what profiles are for, and how Dev Containers that put the environment into the repo work.

C# Dev Kit vs the C# Extension Alone

VS Code has two layers for C#. At the bottom is the open-source C# extension (ms-dotnettools.csharp): a Roslyn-based language server, IntelliSense, navigation, refactorings and the coreclr debugger. On top is the closed-source C# Dev Kit (ms-dotnettools.csdevkit): Solution Explorer, Test Explorer, a project system, the dotnet debug type that fully reads launchSettings.json, and Hot Reload. Dev Kit requires version 2.0 or later of the C# extension and doesn't replace it; it adds to it.

Three things drive the choice. Licensing: as we saw in the first post, Dev Kit is free for commercial teams of up to 5 and needs a Visual Studio subscription beyond that. Project type: Dev Kit doesn't support .NET Framework projects; in such a solution, the dotnet.preferCSharpExtension setting hands project loading to the C# extension and Dev Kit is disabled for that workspace. Need: you can work with the dotnet CLI without Solution Explorer and the test window; Dev Kit's real contribution is a solution view familiar to people coming from Visual Studio. In 2026.2 JetBrains also added Rider's debugger engine to its ReSharper extension for VS Code-compatible editors; it is worth knowing as a third option.

If the repo has more than one solution, Dev Kit asks which one to open; writing the choice into a workspace setting puts everyone on the team on the same solution.

Angular Language Service and TypeScript

TypeScript support comes built into VS Code. For Angular templates you need the Angular Language Service extension (angular.ng-template), maintained by the Angular team: completion, Go to Definition and diagnostics inside templates, and the rename across templates we saw in the fourth post. The Angular CLI already writes this extension as a recommendation into the .vscode/extensions.json of a project created with ng new:

{
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
  "recommendations": ["angular.ng-template"]
}

The Angular docs recommend turning on strictTemplates in tsconfig.json for the latest Language Service features. The extension works with the @angular/language-service and TypeScript versions in the workspace's node_modules; it doesn't need a global Angular installation.

User, Workspace and Folder Settings

VS Code settings are layered, and according to the docs a later layer overrides an earlier one: default, user, remote, workspace, workspace folder; then the same order repeats for language-specific settings, and finally the administrator's policy settings. Primitive and array values are overridden, object values (for example files.exclude) are merged. The practical rule is simple: personal preferences (theme, font, minimap) go in user settings, team decisions (formatter, solution, exclude list) in the repo's .vscode/settings.json.

{
  "dotnet.defaultSolution": "Bookstore.slnx",
  "editor.formatOnSave": true,
  "files.exclude": {
    "**/bin": true,
    "**/obj": true
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

If you want to see the API and Angular as separate root folders, you use a multi-root workspace. The .code-workspace file lists the folders; each folder's own .vscode/settings.json becomes the workspace folder layer:

{
  "folders": [
    { "name": "api", "path": "api" },
    { "name": "web", "path": "web" }
  ],
  "settings": {
    "files.exclude": { "**/bin": true, "**/obj": true }
  }
}

Watch out: language-specific settings override general ones even if their scope is narrower. A "[typescript]" block in user settings overrides the non-language-specific editor.defaultFormatter in the workspace; part of the "I put the setting in the repo but it doesn't work for me" cases comes from here.

Profiles: Separate Setups per Stack

A profile is a bundle of settings, extensions, shortcuts, snippets and UI layout. With C# Dev Kit and the Angular Language Service enabled in a ".NET + Angular" profile, neither loads in a "Writing and Markdown" profile; the editor starts faster and unrelated suggestions don't show up. A profile is associated with a folder or workspace: every time you open that folder, the profile becomes active on its own. In the Profiles editor you create a new profile empty, from an existing profile or from ready-made templates (such as Python, Java and Angular); to apply a setting or an extension to all profiles there are the Apply Setting to all Profiles and Apply Extension to all Profiles actions. Profiles can be exported to a GitHub gist or a file and handed to a teammate.

You can also open one from the command line; the help output of the VS Code on this machine:

$ code --help
Visual Studio Code 1.145.2
  --profile <profileName>                    Opens the provided folder or
                                             workspace with the given profile
                                             and associates the profile with
                                             the workspace. If the profile does

A profile's own settings.json lives under ~/Library/Application Support/Code/User/profiles/<profile ID>/ on macOS and is created only when you change a setting in that profile.

Dev Container: Putting the Environment in the Repo

A Dev Container defines the development environment as a container image and a JSON (JavaScript Object Notation) file. With the Dev Containers extension, VS Code opens the repo inside the container: the editor UI runs on your machine, the language services, terminal and debugger run in the container. For Bookstore we used the official image with the .NET 10 SDK and the Node LTS feature:

{
  "name": "Bookstore (.NET 10 + Node LTS)",
  "image": "mcr.microsoft.com/devcontainers/dotnet:10.0",
  "features": {
    "ghcr.io/devcontainers/features/node:1": {
      "version": "lts"
    }
  },
  "forwardPorts": [5046, 4200],
  "portsAttributes": {
    "5046": { "label": "Bookstore.Api" },
    "4200": { "label": "Angular dev server", "onAutoForward": "openBrowser" }
  },
  "postCreateCommand": "dotnet restore api/Bookstore.Api.csproj",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-dotnettools.csdevkit",
        "angular.ng-template",
        "editorconfig.editorconfig"
      ]
    }
  }
}

We brought the same file up with the Dev Container CLI. The image was pulled, the Node feature was installed on top, and postCreateCommand ran; it took 47 seconds end to end on this machine:

$ devcontainer up --workspace-folder .
[2026-11-08T12:07:52.266Z] @devcontainers/cli 0.90.0. Node.js v24.22.2. darwin 27.0.0 arm64.
[2026-11-08T12:08:38.447Z] Running the postCreateCommand from devcontainer.json...
[2026-11-08T12:08:39.132Z]   Restored /workspaces/repo/api/Bookstore.Api.csproj (in 24 ms).
{"outcome":"success","containerId":"240d73269c78e04640aff146226bdc2b973b95ff4874aefcacc93f8f48c1a6ad","remoteUser":"vscode","remoteWorkspaceFolder":"/workspaces/repo", ...}

$ devcontainer exec --workspace-folder . bash -lc 'dotnet --version; node --version; npm --version; whoami; pwd'
10.0.403
v26.13.0
11.20.0
vscode
/workspaces/repo

The first answer to Guess First: on the new developer's machine Docker and VS Code (with the Dev Containers extension) are enough; the SDK, Node and extensions come with the container. Note the detail in the output: the host has .NET SDK 10.0.402, the container 10.0.403. Because everyone uses the same image, the "which SDK do I have" question goes away; but the image tag (10.0) is a floating tag, so what is 10.0.403 today may be a different patch tomorrow. If you want an exact version, pin the image by digest or use global.json in the repo.

Port Forwarding: Reaching the API in the Container

The second question. We started the API inside the container with the http profile and sent a request first from inside, then from the host:

$ devcontainer exec --workspace-folder . bash -lc 'cd api && dotnet run --launch-profile http'
      Now listening on: http://localhost:5046
      Hosting environment: Development
# from inside the container
{"environment":"Development","shippingProvider":"(not set)","pid":657}

# from the host
$ curl -sS --max-time 3 http://localhost:5046/api/info
curl: (7) Failed to connect to localhost port 5046 after 0 ms: Couldn't connect to server
$ docker port idework-devcontainer
$

The API runs in the container but can't be reached from the host, and the container has no published ports. The reason is that forwardPorts is a VS Code feature, not Docker's port publishing: when VS Code connects to the container, it carries traffic through its own server and gives the host access even to an app bound to localhost; the CLI on its own doesn't do that. The VS Code docs give appPort as a second way; it uses Docker's port publishing and may require the app to listen on all interfaces instead of localhost. If you use the Dev Container outside VS Code (in CI (Continuous Integration), in another editor), you need to know this difference; inside VS Code you see the forwarded port and its host counterpart in the Ports panel.

Remote SSH, WSL and the Agent Windows

Dev Container's siblings use the same architecture: the Remote - SSH (Secure Shell) extension connects to a Linux server, the WSL (Windows Subsystem for Linux) extension to a Linux distribution on Windows; the VS Code server runs on the remote side and most extensions are installed there. Running .NET and Node inside WSL on Windows is a popular setup for file system performance and Linux-specific tools.

VS Code's real change in 2026 is on the agent side. The integrated browser, introduced in 1.109, opens web pages inside the editor and lets the agent interact with the page; with 1.120 (May 13, 2026) the Agents window reached the Stable channel; 1.140 added the ability for agent sessions to run isolated in the project's Dev Container; and 1.145 on November 4, 2026 brought previews through the integrated browser over remote agent hosts and automations scheduled with cron. These features change quickly; the state of AI (Artificial Intelligence) assistants in IDEs (Integrated Development Environment) is the subject of the ninth post.

In Production

Pin the Dev Container image. The dotnet:10.0 tag points to a new image with every patch; for a reproducible environment use a digest or build your own image in your own registry.

Keep the extension list in the repo: customizations.vscode.extensions in devcontainer.json and .vscode/extensions.json. Extension security and enterprise restrictions are in the eighth post.

Don't keep secrets in the container. Don't put connection strings or tokens in devcontainer.json or in the image; use user secrets, environment variables or a secret manager. The Dev Container file is a file everyone sees in the repo.

On macOS and Windows, Docker runs in a virtual machine; syncing large node_modules and obj folders with the host can be slow. Keeping these folders in a volume of the container is a known speed-up.

Common Pitfalls

Startup slowed down by a pile of extensions. Every extension loads on an activation event; "Developer: Show Running Extensions" in the Command Palette shows which one takes how long, and "Help: Start Extension Bisect" finds the problematic extension by halving the set. Separating profiles per stack reduces this problem at the root.

Forgetting port forwarding in a Dev Container, or expecting it outside VS Code. Real output: the API running in the container couldn't be reached from the host in the environment brought up with the CLI; forwardPorts is VS Code's job, appPort is Docker's.

Committing personal settings to the workspace. If a font size or theme ends up in .vscode/settings.json, everyone's editor on the team changes.

Forcing Dev Kit on a .NET Framework project. Dev Kit doesn't support these projects; hand project loading to the C# extension with dotnet.preferCSharpExtension.

Not knowing the precedence of language-specific settings. A "[typescript]" block in user settings overrides the general formatter setting in the workspace.

A Small Experiment

★☆☆ Add .vscode/settings.json and .vscode/extensions.json to your own repo; put only settings that are team decisions in them. Check which of your personal settings have leaked into the repo.

★★☆ Create two profiles named ".NET + Angular" and "Docs", separate their extension lists and compare VS Code's startup time with the Developer: Startup Performance command.

★★★ Adapt this post's devcontainer.json to your own repo, pin the image by digest and run the API and Angular inside the container. Bring it up once with VS Code and once with the Dev Container CLI and compare port access.

We turned VS Code into a working environment. In the next post we move to JetBrains Rider: the ReSharper engine's inspections, the decompiler and Source Link, inspecting the SQL (Structured Query Language) that Entity Framework Core (EF Core) generates with the built-in database tools, and what Rider 2026.2 brought.