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.
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
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.
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).
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.
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”.
Next, let’s try adding gRPC service with the help of copilot.
- I need to add gRPC service to existing Program.cs (REST api / web)
- dont add Startup.cs file. But keep using the Program.cs file and add necessary packages and codes in it
- Create a simple Hello gRPC service
added new folders Protos
and Services
. Created hello.proto
& HelloService.cs
file as copilot instructed and updated namespace to mine (WebApplication3).
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.
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.
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.