# Setting Up a Development Environment With Docker For .NET With Postgres SQL

In this article, we are going to set up a developer-friendly environment for the asp.net MVC application in visual studio code. In addition, we are going to containerize the MVC application with postgres SQL database along with Pgadmin4. When it comes to development debugging is a crucial task. we also set up debugging functionality to code in the docker container. So, without wasting time let's get started!

### What will we learn from this article?

This article guide teaches you how to set up containerized .NET application using docker and how to use it in a developer environment with the debugger.

* Create a simple dot net MVC application
    
* Create a new Dockerfile which contains instructions required to build a. Net image.
    
* Set up docker-compose which set up postgres sql database, pgadmin to monitor postgres database.
    
* Set up vs code debugger in the visual studio code editor.
    

### Docker

Docker is a software platform that enables rapid development, testing, and deployment of applications. Docker encapsulates software into standardized units called containers that have everything (i.e all dependencies) the software needs to run including libraries, system tools, code, runtime, and settings.

### Container

A container is a standard unit of software that packages up code and all its dependencies to that application and runs quickly and reliably from one computing environment to another. We can think container as an isolated process in the operating system which has its own networking and file system. A Docker Container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings.

### DotNet

.NET is an open-source developer platform, created by Microsoft, for building many different types of applications. .Net is a free, cross-platform, open-source developer platform for building different types of applications.

With .NET, you can use multiple languages, editors, and libraries to build for web, mobile, desktop, games, IoT, and more. You can write .NET apps in C#, F#, and many other dot net languages.

### Software Prerequistes

Before diving into the coding section, we have to make sure the following software is installed. I am using windows 11 for this guide. You can use other operating systems as well.

1. Docker Desktop with docker-compose
    
2. Visual Studio Code
    
3. Dotnet Sdk 7
    
4. Any Web Browser
    

### Creating New .NET MVC Application

For our sample application, let's create a simple application from a template using .NET CLI. Create a directory in our local machine named sampleapp. Open a cmd and change to that directory. Run the following `dotnet new` command to create a C# app using the ASP.NET MVC template.

```bash
mkdir sampleapp
cd sampleapp
dotnet new mvc -n sampleapp -o src --no-https
```

Above command will create an src file inside sampleapp folder. Furthermore, inside src, we will get asp.net MVC project folders and files. The further structure will look like following

```bash
├── src
├── Controllers
│ └── HomeController.cs
├── Models
│ └── ErrorViewModel.cs
├── Views
│ └── Home
│     └── Index.cshtml
│     └── Privacy.cshtml
├── Program.cs
├── Properties
│ └── launchSettings.json
├── appsettings.Development.json
├── appsettings.json
├── sampleapp.csproj
├── obj
│ ├── sampleapp.csproj.nuget.dgspec.json
│ ├── sampleapp.csproj.nuget.g.props
│ ├── sampleapp.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.nuget.cache
└── wwwroot
  ├── css
  ├── favicon.ico
  ├── js
  └── lib
```

Now, Let's start our application and make sure it's running properly. In the command prompt navigate to the src folder and use the `dotnet run` command.

```bash
cd /path/to/sampleapp/src
dotnet run --urls http://localhost:5000
```

Ouput similar to the following appears.

```bash
Building...
info: Microsoft.Hosting.Lifetime[0]
    Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
    Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
    Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
    Content root path: C:\Users\username\sampleapp\src
```

Now you will see the following page in the browser:

![](https://docs.docker.com/language/dotnet/images/dotnet-app-verify-build.png align="center")

If you get the result as the above image then you will install mvc project correctly and you

are ready to go further.

### Create a Dockerfile

In `sampleapp` directory, create a file named `Dockerfile` And paste the following code.

```plaintext
FROM mcr.microsoft.com/dotnet/sdk:7.0 as debug

#install debugger for NET Core
RUN apt-get update
RUN apt-get install -y unzip
RUN curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -l ~/vsdbg

RUN mkdir /src/
WORKDIR /src/

COPY ./src/testapp.csproj /src/sampleapp.csproj
RUN dotnet restore

COPY ./src/ /src/
RUN mkdir /out/
RUN dotnet publish --no-restore --output /out/ --configuration Release
EXPOSE 80
CMD dotnet run --urls "http://0.0.0.0:80"
```

The above Dockerfiel is used for building and running a .NET Core application in a Docker Container.

It starts with the base image `mcr.microsoft.com/dotnet/sdk:7.0` and tags it as `debug`.

In summary above docker file will do the following tasks step by step one at a time.

1. Installs the debugger for .NET Core by updating the package list and installing unzip, then downloading and installing the latest version of vsdbg.
    
2. Creates a directory '/src/' as the working directory.
    
3. Copies the project file 'sampleapp.csproj' to '/src/' and restores the dependencies.
    
4. Copies the source code to '/src/' and runs the publish command to build and output the application to '/out/'.
    
5. Exposes port 80 for external access.
    
6. Runs the application with the command `dotnet run --urls "`[`http://0.0.0.0:80`](http://0.0.0.0:80)`"`.
    

### Create docker-compose.yml File

Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to define an application’s services, networks, and volumes in a single file, then start and stop the services using a single command.

Open the `sampeapp` directory in vs code and create a file named `docker-compose.yml` . Copy and paste the following contents into the file.

```yaml
version: "3.0"
services:
  db:
    image: postgres
    restart: always
    ports:
      - "5432"
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
      POSTGRES_USER: sachin

  pg_admin:
    image: dpage/pgadmin4
    restart: always
    ports:
      - "5555:80"
    volumes:
      - pg_admin:/var/lib/pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: sachin.maharjan@dishhome.com.np
      PGADMIN_DEFAULT_PASSWORD: password

  web:
    container_name: csharp
    build:
      context: .
      target: debug
    ports:
     - "5000:80"
    volumes:
      - ./src:/src/
    depends_on:
     - db

volumes:
  postgres:
  pg_admin:
```

Save and close the docker-compose.yml file. Add the `.dockerignore` file and paste the following code.

```bash
**/bin/
**/obj/
```

The sampleapp directory structure should now look like

```bash
├── samplefile
│ ├── src/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── docker-compose.yml
```

The `.dockerignore` the file is used in a Docker build context to specify files or directories that should be excluded from the build context. This helps to reduce the size of the build context and improve the build time by excluding files and directories that are not necessary for the build. Any files or directories listed in the .dockerignore file will not be included in the build context that is sent to the Docker daemon for building the image.

The `docker-compose.yml` the file describes a multi-container application. In our `docker-compose.yml` file, we have three services `db`, `pgadmin` and `web`.

* 'db' is a PostgreSQL database service, which uses the official PostgreSQL image. It will be exposed on port 5432 and the data will be persisted in a Docker volume named 'postgres'. The environment variables 'POSTGRES\_PASSWORD' and 'POSTGRES\_USER' are set to 'password' and 'sachin' respectively.
    
* 'pg\_admin' is a web-based database administration tool, using the image 'dpage/pgadmin4'. It will be exposed on port 5555 and the data will be persisted in a Docker volume named 'pg\_admin'. The environment variables 'PGADMIN\_DEFAULT\_EMAIL' and 'PGADMIN\_DEFAULT\_PASSWORD' are set to '[**sachin.maharjan@dishhome.com.np**](mailto:sachin.maharjan@dishhome.com.np)' and 'password' respectively.
    
* 'web' is a .NET Core web application service. The service is built from the current directory and the build context is set to the 'debug' target. The service will be exposed on port 5000 and the source code will be mounted as a volume in the container at the '/src/' directory. The 'depends\_on' key ensures that the 'db' service is started before the 'web' service.
    
* The 'volumes' section at the bottom defines two named volumes: 'postgres' and 'pg\_admin'.
    

### Database Connection

Let's add the package to allow the app to talk to a database and update the source files. On your local machine, open a terminal, change the directory to the src directory and run the following command:

```bash
cd /path/to/dotnet-docker/src
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
```

In the `src` directory, create a `Models` folder. Inside the `Models` folder create a file named `Student.cs` and add the following code to `Student.cs`:

```csharp
using System;
using System.Collections.Generic;
namespace sampleapp.Models
{
  public class Student
  {
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
  }
}
```

Save and close the `Student.cs` file.

In the `src` directory, create a `Data` folder. Inside the `Data` folder create a file named `SchoolContext.cs` and add the following code to `SchoolContext.cs`:

```csharp
using Microsoft.EntityFrameworkCore;
namespace sampleapp.Data
{
   public class SchoolContext : DbContext
   {
      public SchoolContext(DbContextOptions<SchoolContext> options) : base(options) { }
      public DbSet<Models.Student>? Students { get; set; }
   }
}
```

Save and close the `SchoolContext.cs` file.

In the `Program.cs` file located in the `src` directory, replace the contents with the following code:

```csharp
using Microsoft.EntityFrameworkCore;
using sampleapp.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
   options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<ApplicationDbContext>();
    if (context.Database.GetPendingMigrations().Any())
    {
        context.Database.Migrate();
    }
}
app.Run();

     
```

Save and close the `Program.cs` file.

In the `appsettings.json` file located in the `src` directory, replace the contents with the following code:

```bash
{
   "Logging": {
       "LogLevel": {
           "Default": "Information",
           "Microsoft": "Warning",
           "Microsoft.Hosting.Lifetime": "Information"
       }
   },
   "AllowedHosts": "*",
   "ConnectionStrings": {
       "SchoolContext": "Host=db;Database=my_db;Username=sachin;Password=password"
   }
}
```

Save and close the `appsettings.json` file.

In the `Index.cshtml` file located in the `src\Pages` directory, replace the contents with the following code:

```bash

@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

<div class="row mb-auto">
    <p>Student Name is @ViewBag.Student</p>
</div>
```

Save and close the `Index.cshtml` file.

In the `Index.cshtml.cs` file located in the `src\Pages` directory, replace the contents with the following code:

```bash
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using sampleapp.Models;

namespace testapp.Controllers;

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly SchoolDbContext _db;
    public HomeController(ILogger<HomeController> logger, SchoolDbContext db)
    {
        _logger = logger;
        _db = db;
    }

    public IActionResult Index()
    {
        var student = _context.Students?.Where(d=>d.ID==1).FirstOrDefault();
            this.StudentName = $"{s?.FirstMidName} {s?.LastName}";
       ViewBag.Student = student;
        return View();
    }

    public IActionResult Privacy()
    {
        return View();
    }

    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
}
```

Save and close the `Index.cshtml.cs` file.

Now, add migration command to create database and students table in a database named my\_db.

```bash
dotnet-ef migrations add "InitalMigration"
```

Note: you need to install dotnet-ef utility tool, for this you can install this using

`donet tools install --global dotnet-ef`

Now, up the all container using docker-compose up command. cd to sampleapp directory and run the following command.

```bash
cd path to sampleapp
docker-compose up --build
```

If every thing is ok then all three containers web, database and pgadmin will start. Now, go to browser and enter url localhost:5555. you will see the login page of pg admin.

![](https://www.pgadmin.org/static/docs/pgadmin4-6.18-docs/_images/oauth2_login.png align="center")

Now, Login into pgadmin using email and password setup in `docker-compose.yml` file. After that, you will redirect to the home page.

![](https://www.pgadmin.org/static/docs/pgadmin4-6.15-docs/_images/welcome.png align="center")

Now right-click on the server and click on to register server option. Then a dialog box will open. In that section, you have to provide the hostname as `db` port name 5432 and username as sachin and password as password. All credentials are already defined in `docker-compose.yml the` file.

Now, Open the server menu you will see the database name my\_db. Now open that my\_db menu you will see the schema section. Under the schema section, you will see tables. Now add a row in the table named students.

so, If you open a browser and enter localhost:5000 you will see the home page with the first name and last name that you have inserted in the database.

### Enabling Debugging In VS Code

For enabling debugging in vs code, first, we need the following extensions in vs code

1. VS Code Extension for Docker
    
2. VS Code Launch configuration
    
3. C# extension for VS Code
    

Click on to debug button on the right of the visual studio code. Then You will have the option to create a launch.json file. Click on it to create a launch.json.

Then it will create .vscode under sampleapp directory. when we open `.vscode` directory we will see the launch.json file. Open the file and paste the code below:

```bash
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Docker Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "${command:pickRemoteProcess}",
            "pipeTransport": {
                "pipeProgram": "docker",
                "pipeArgs": [ "exec", "-i", "csharp" ],
                "debuggerPath": "/root/vsdbg/vsdbg",
                "pipeCwd": "${workspaceRoot}",
                "quoteArgs": false
            },
            "sourceFileMap": {
                "/src": "${workspaceRoot}/src/"
            }
        },
       
    ]
}
```

Now, if you put breakpoints in any c# code and hit the green run button on debug section. Then execution stops at that breakpoint. But you have to choose the correct process during the prompt which pops out while you click the green run button.

![](https://code.visualstudio.com/assets/docs/editor/debugging/server-ready.gif align="center")

The above animation is for node.js, I have kept it just to simulate how it is done.

That is, it! I hope you found this helpful. If you have any questions or are confused about anything, you can reach out to me or you can check out the sample project on [GitHub](https://github.com/lujamaharjan/DotnetDocker).

Thank you!

Happy Coding :)
