VS Code with GitHub Copilot

and Visual Studio & Docker support

--

Last time we learned a bit about .NET and C#. Today we will continue with Visual Studio, Docker, and GitHub Copilot. So, let’s start with downloading and installing Visual Studio — Community Edition.

Visual Studio download & install

We can create a new .NET project → ASP.NET Core Web API (C#) with all the default options.
- .NET 6.0 (8.0 if you want), No authentication, Configure for HTTPS
- Enable Docker, Enable OpenAPI support, Use Controllers

Create new .NET project (ASP.NET Core Web API)

Docker

Docker is an open-platform used for developing, shipping, and running applications in containers.

After the project is ready and the solution is open, you might see it is already checking if Docker Desktop is running. You can run the project manually by clicking on ▶ button with the text “Container (Dockerfile)” and checking the Swagger UI.

Docker container run → Swagger test → GET /WeatherForecast

Containers are lightweight, portable, and self-sufficient units that bundle all the necessary components, such as code, runtime, libraries, and dependencies, needed to run an application.

If we dig a bit deeper into Dockerfile we can see that it uses layers. First, it uses aspnet:6.0 for the base layer and assigns a name to this stage, which can be referenced later. Sets the working directory inside the container to /app, and exposes ports 80 and 443, indicating that the container will listen on these ports.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

Then sdk:6.0 for build layer, to prepare everything needed for the project. ARG: Declares a build argument BUILD_CONFIGURATION with a default value of Release. Uses COPY to copy necessary files, RUN to execute commands like “dotnet restore” or “dotnet build”.

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["WebApplication3/WebApplication3.csproj", "WebApplication3/"]
RUN dotnet restore "./WebApplication3/WebApplication3.csproj"
COPY . .
WORKDIR "/src/WebApplication3"
RUN dotnet build "./WebApplication3.csproj" -c $BUILD_CONFIGURATION -o /app/build

Afterward publishes the application using the previous build stage and saves the output in “/app/publish” directory.

FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./WebApplication3.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false

The final stage creates the production image. It uses base instead of build or publish. However, copies the output from publish and sets ENTRYPOINT for the application when the container starts.
- In this case, it's running the .NET application (WebApplication3.dll).

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication3.dll"]

When using a multi-stage build approach docker image size is smaller, build time is faster. Also, it is considered better to maintain the Dockerfile and potential vulnerabilities in build tools or intermediate files are minimized.

Lifecycle: #1 Dockerfile → #2 Image → #3 Container

Docker Compose (Container Orchestrator Support)

Docker Compose allows you to use a YAML file to configure services, networks, and volumes required for your application.

While Docker makes life easy, Docker-Compose makes it easier. In VS, first right-click on project file and choose Add → then Container Orchestrator Support with options Docker Compose, Linux (Target OS).

Project file → Add → Container Orchestrator Support → Docker Compose → Linux (Target OS)

TargetOS : specifies where the Docker container will run (Linux or Windows)
There could be differences in binaries, env variables, compatibility, performance.

Let’s dig a bit deeper into the docker-compose.yml file. Firstly, it defines which version to use version: '3.4'. Secondly, under the services section, you define the services that make up your application. For us it is only webapplication3` (name), that uses image:${variable-}webapplication3. Also, build defines use the Dockerfile in the same directory (context: .) to build the image.

version: '3.4'

services:
webapplication3:
image: ${DOCKER_REGISTRY-}webapplication3
build:
context: .
dockerfile: WebApplication3/Dockerfile

There is also a docker-compose.override.yml which overrides docker-compose.yml we just saw earlier. We can see the same version, services but also extra environment, ports, and volume configuration. volumes section mounts paths on the host to paths in the container. Host:Container ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro ro at the end means the volume is read-only.

version: '3.4'

services:
webapplication3:
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=https://+:443;http://+:80
ports:
- "80"
- "443"
volumes:
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

For today we only have 1 service (webapplication3) but if we need more containers for maybe database or frontend we will also define them in docker-compose.yml.

GitHub copilot

GitHub Copilot is an AI-powered coding assistant developed collaboratively by GitHub and OpenAI.

With copilot, you will get code suggestions in many programming languages with contextual understanding. Also, it is free for students.

Visual Studio → Extension → GitHub copilot → View → Copilot chat

One nice thing is you can ask questions using the “chat”. For example, asking copilot “to add a persisted docker volume to docker-compose file”.

GitHub copilot → chat → persisted docker volume

Next, let’s try adding gRPC service with the help of copilot.

  1. I need to add gRPC service to existing Program.cs (REST api / web)
  2. dont add Startup.cs file. But keep using the Program.cs file and add necessary packages and codes in it
  3. Create a simple Hello gRPC service
GitHub copilot → add gRPC service to existing code (Web API)
dotnet add package

added new folders Protos and Services. Created hello.proto & HelloService.cs file as copilot instructed and updated namespace to mine (WebApplication3).

hello.proto & HelloService.cs

Also, if the project file is not updated automatically include the following

<ItemGroup>
<Protobuf Include="Protos\hello.proto" GrpcServices="Server" />
</ItemGroup>

Finally, add gRPC service and map HelloService in Program.cs, so it works.

Program.cs → add gRPC

The best thing is, if you get an error, you can always ask copilot just by clicking on a button.

In this case, I forgot builder.Services.AddControllers(); so I got an error on L#18 endpoints.MapControllers(); when trying to map them. So after adding it, everything becomes normal.

GitHub copilot error → ask copilot (fix)

TL;DR

So far, we’ve used Visual Studio to create Web API, tested simple GET request with Swagger UI, and ran it in a Docker container. While Dockerfile is used to build the image, docker-compose is used to manage the container. In the last part, we’ve used GitHub copilot to get some answers and code suggestions, which boosts developer productivity massively.

Let’s fly together!

--

--

Билигүн.Б (Програмч аав)
2B +1% better 2day

I am who I am... || өөрийнхөөрөө байхаас ичихгүй