Development Environment Setup

microservices csharp aspnetcore

This is the first post in the microservices architecture series.

The goal of this exercise is to make sure that we have the developer environment setup.

We should be able to run and debug the skeleton application using docker.

Development Environment for Docker apps

The following applications should be installed:-

Sample Application

Download the sample app from here.

It contains 3 sample projects:-

  • SampleApi1

  • SampleApi2

  • SampleWebApp

Docker Setup

Dockerfile

Each of the three projects has a Dockerfile and a Dockerfile.develop.

Following is the multi-stage Docker file.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80 % (1)

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src

# It's important to keep lines from here down to "COPY . ." identical in all Dockerfiles
# to take advantage of Docker's build cache, to speed up local container builds
COPY "InitialSetup.sln" "InitialSetup.sln"
COPY ["SampleApi1/SampleApi1.csproj", "SampleApi1/"]
COPY ["SampleApi2/SampleApi2.csproj", "SampleApi2/"]
COPY ["SampleWebApp/SampleWebApp.csproj", "SampleWebApp/"]

COPY "docker-compose.dcproj" "docker-compose.dcproj"

COPY "NuGet.config" "NuGet.config"

RUN dotnet restore "InitialSetup.sln"

COPY . .
WORKDIR "/src/SampleApi1"
RUN dotnet publish --no-restore -c Release -o /app

FROM build AS publish

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

The COPY commands are the same for all three projects.
This makes use of the way layering works in Docker.
This is an optimization that makes the development containers load faster as a large part of the image is cached.

The docker compose file looks like to following:-

version: '3.4'

services:

  sqldata:
    image: mcr.microsoft.com/mssql/server:2019-latest

  sample-api1:
    image: ${DOCKER_REGISTRY-}sample-api1
    build:
      context: .
      dockerfile: SampleApi1/Dockerfile
    depends_on:
      - sqldata

  sample-api2:
    image: ${DOCKER_REGISTRY-}sample-api2
    build:
      context: .
      dockerfile: SampleApi2/Dockerfile
    depends_on:
      - sqldata

  sample-webapp:
    image: ${DOCKER_REGISTRY-}sample-webapp
    build:
      context: .
      dockerfile: SampleWebApp/Dockerfile
    depends_on:
      - sample-api1
      - sample-api2
      - sqldata

The following is the docker-compose.override file for development.

services:

  sqldata:
    environment:
      - SA_PASSWORD=Pass@word
      - ACCEPT_EULA=Y
    ports:
      - "5433:1433"
    volumes:
      - eshop-sqldata:/var/opt/mssql

  sample-api1:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://0.0.0.0:80
    ports:
      - "5023:80"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
  sample-api2:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://0.0.0.0:80
    ports:
      - "5287:80"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

  sample-webapp:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://0.0.0.0:80
      - SampleApi1=http://host.docker.internal:5023
      - SampleApi2=http://host.docker.internal:5287
    ports:
      - "5122:80"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro

volumes:
  eshop-sqldata:
    external: false

Nothing special to note in these files. They are standard docker-compose files for dotnet.

Visual Studio Setup

  • To execute or debug the code in Visual Studio, the docker-compose.dcproj needs to be setup as a startup project.

Visual Studio Code Setup

Extensions

The following extensions need to be installed:-

  • C#

  • Docker

  • Dev Containers (Optional)

Execute and Debug

There are two files in the .vscode folder: tasks.json and launch.json which are setup to help execute and debug the project.

tasks.json
{
    "label": "build dotnet",
    "type": "shell",
    "command": "dotnet",
    "args": [
        "build",
        "${workspaceFolder}/InitialDevSetup.sln"
    ],
    "problemMatcher": [
        "$msCompile"
    ]
},
{
    "label": "Run docker-compose up",
    "type": "docker-compose",
    "dockerCompose": {
        "up": {
            "detached": true,
            "build": true
        },
        "files": [
            "${workspaceFolder}/docker-compose.yml",
            "${workspaceFolder}/docker-compose.override.yml"
        ]
    }
}
  • The build dotnet task can be used to build the application.

  • The Run docker-compose up task can be used to execute the application.

To attach a debugger, there are 3 options setup in launch.json, one for each project.

These will appear in the debug configuration list of VS Code.

launch.json
{
    "name": "Attach SampleApi1",
    "type": "docker",
    "request": "attach",
    "platform": "netCore",
    "containerName": "1_initialdevsetup-sample-api1-1",
    "sourceFileMap": {
        "/src": "${workspaceFolder}"
    }
},
{
    "name": "Attach SampleApi2",
    "type": "docker",
    "request": "attach",
    "platform": "netCore",
    "containerName": "1_initialdevsetup-sample-api2-1",
    "sourceFileMap": {
        "/src": "${workspaceFolder}"
    }
},
{
    "name": "Attach Sample Web App",
    "type": "docker",
    "request": "attach",
    "platform": "netCore",
    "containerName": "1_initialdevsetup-sample-webapp-1",
    "sourceFileMap": {
        "/src": "${workspaceFolder}"
    }
}

The project should now be ready to run on the developer machine.