Dockerize ASP.NET Core 8 App with SQL Server Using Docker Compose

Last Update: October 8, 2024
Dockerize ASP.NET Core 8 App with SQL Server Using Docker Compose
Table of Contents
Contributors
Picture of Vivasoft Team
Vivasoft Team
Tech Stack
0 +
Want to accelerate your software development company?

It has become a prerequisite for companies to develop custom software products to stay competitive.

Introduction

In this tutorial, we’ll walk through the process of Dockerizing an ASP.NET Core application and an SQL Server database using Docker Compose. By the end of this guide, you’ll have a clear understanding of how to containerize your ASP.NET Core application and its associated SQL Server database for easy deployment and scalability.

Brief about Docker Compose?

Docker Compose is a tool for defining and managing multi-container Docker applications. It allows you to define the services, networks, and volumes of your application in a single docker-compose.yml file, and then manage them with simple commands. This is particularly useful for applications that consist of multiple services that need to interact with each other, such as a web server, a database, and a cache.

Dockerize ASP.NET Core 8 App with SQL Server Using Docker Compose: A Step-by-Step Guide

Prerequisites:

Before we begin, ensure that you have the following prerequisites installed on your system:

  • Docker Desktop (or Docker Engine) – for running Docker containers.
  • ASP.NET Core 8.0 SDK – for building and running ASP.NET Core applications.
  • Visual Studio 2022 or newer
  • Basic knowledge of
  • Docker Commands
  • Basic knowledge of
  • ASP.NET Core Web API and Entity Framework Core.

Step 1: Set up your ASP.NET Core Web API Application

Start by creating a new ASP.NET Core Web API project or use an existing one or you can create using Visual Studio. For using Visual Studio make sure you enable the checkbox Enable Docker.

Choose App Template

Figure: Choose App Template

After creating the project Open your terminal or command prompt and navigate to your project directory. you‘ll get this project structure.

Visualize project files in the console

Figure: Visualize project files in the console

Step 2: Create a Controller and Seed Data

Create a controller to expose endpoints for testing. Additionally, seed some initial data into your database.
Model Class: Create a model class for the Product entity.
public class Product

				
					{
    public int Id { get; set; }

    public required string Name { get; set; }
}
				
			

Controller: Create a controller named ProductController to handle API requests.

				
					[Route("api/[controller]")]
[ApiController]
public class ProductController(ApplicationDbContext context) : ControllerBase
{
    private readonly ApplicationDbContext _context = context;

    [HttpGet]
    public ActionResult Greetings() => Ok("Hello, I'm alive!");

    [HttpGet("{id}")]
    public async Task<ActionResult<ProductModel>> GetItem(int id)
    {
        _context.Database.EnsureCreated();

        var item = await _context.Products.FindAsync(id);

        if (item == null)
            return NotFound();

        return item;
    }
}
				
			

Step 3: Install EF Core and add migration to the application

EF Core is an Object/Relational Mapping (O/RM) framework, an enhanced version of ADO.NET, which automates data storage and retrieval from the database. To Install EF Core in our app install the following 2 packages,

* Install EF Core DB Providers

Go to Tools > NuGet Package Manager > Manage NuGet Packages for Solution and search for Microsoft.EntityFrameworkCore.SqlServer. When it shows up, select it and install it to your app

Figure: Nuget package – Microsoft.EntityFrameworkCore.SqlServer

* Install EF Core Tools
Next, search for Microsoft.EntityFrameworkCore.Tools. Once you get the result, install it and add the migration

Now, Add some seed data into your DbContext to populate the database with initial records.

				
					public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : DbContext(options)
{
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>().HasData(
            new Product { Id = 1, Name = "Product 1" },
            new Product { Id = 2, Name = "Product 2" }
        );
    }
}
				
			

Step 4: Look into the docker file and get an explanation

If you go to your project directory you will see the docker file for the application that was generated by Visual Studio itself.

				
					FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER app
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Product.API/Product.API.csproj", "Product.API/"]
RUN dotnet restore "./Product.API/./Product.API.csproj"
COPY . .
WORKDIR "https://cdn.vivasoftltd.com/src/Product.API"
RUN dotnet build "./Product.API.csproj" -c $BUILD_CONFIGURATION -o /app/build

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

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


				
			

Here is the explanation of this Docker file:

Base Stage (base):

  • Base image: mcr.microsoft.com/dotnet/aspnet:8.0
  • Sets the user to the app and working directory to /app.
  • Exposes port 8080, indicating that the container will listen for incoming connections on this port.

Build Stage (build):

  • Base image: mcr.microsoft.com/dotnet/sdk:8.0
  • Defines an argument BUILD_CONFIGURATION with a default value of Release.
  • Sets the working directory to /src.
  • Copies the project file (Product.API.csproj) into the container’s /src/Product.API/ directory.
  • Runs dotnet restore to restore the dependencies specified in the project file.
  • Copies the entire application source code into the container.
  • Sets the working directory to /src/Product.API.
  • Runs dotnet build to build the application, specifying the build configuration (Release) and output directory (/app/build).

Publish Stage (publish):

  • Inherits from the build stage.
  • Defines an argument BUILD_CONFIGURATION with a default value of Release.
  • Runs dotnet publish to publish the application, specifying the build configuration (Release), output directory (/app/publish), and disabling the use of the app host (/p:UseAppHost=false).
  • This step prepares the application for deployment.

Final Stage (final):

  • Base image: base stage (mcr.microsoft.com/dotnet/aspnet:8.0).
  • Sets the working directory to /app.
  • Copies the published output from the publish stage into the container’s working directory.
  • Specifies the entry point for the container, which is the command to run when the container starts. In this case, it’s dotnet Product.API.dll, indicating that the application executable (Product.API.dll) will be run using the .NET runtime.

Now let’s write a docker file for our SQL server database.

				
					# Use the official SQL Server image
FROM mcr.microsoft.com/mssql/server:latest AS sqlserver
ENV ACCEPT_EULA=Y 
ENV SA_PASSWORD=myPassword1!

# Add optional environment variable for database name
ENV MSSQL_DBNAME=product_db
				
			

Here is the explanation of this db.Docker file:

Base Image:

    • FROM mcr.microsoft.com/mssql/server:latest AS sqlserver: Specifies the base image to use for the SQL Server container. In this case, it uses the official Microsoft SQL Server image from the Microsoft Container Registry (mcr.microsoft.com/mssql/server), tagged as “latest”. The alias “sqlserver” is used for this stage.

Environment Variables:

    • ENV ACCEPT_EULA=Y: Sets the environment variable ACCEPT_EULA to “Y”, indicating acceptance of the End-User License Agreement (EULA) for the SQL Server image.
    • ENV SA_PASSWORD=myPassword1!: Sets the environment variable SA_PASSWORD to “myPassword1!”, which is the password for the sa (System Administrator) account. This is required by SQL Server to be able to start.
    • ENV MSSQL_DBNAME=product_db: (Optional) Sets the environment variable MSSQL_DBNAME to “product_db”, specifying the name of the default database to be created when the SQL Server container starts. This can be customized as needed.

These environment variables are essential for configuring the SQL Server container properly. ACCEPT_EULA acknowledges the license agreement, SA_PASSWORD sets the password for the sa account, and MSSQL_DBNAME allows you to specify the default database name.

Step 5: Create Docker Compose File

Next, create a docker-compose.yml file in your project directory using Visual Studio. This file will define the services required for running your ASP.NET Core application and SQL Server database.
Right-click the project file then goto add then ‘Container Orchestrator Support’

Figure: Add Container 


Then Choose Docker Compose and Click OK.

Figure: Choose Docker Compose

Then Select Linux and click ok. Here we choose Linux as Linux is the preferred choice for container orchestration due to its native support, performance, compatibility, flexibility, and cost-effectiveness

Figure: Choose Targer OS

Then you’ll see the docker-compose.yml file in your solution folder

Figure: Solution of docker-compose

The default generated compose file will look like this

Now, let’s update the compose file as below

				
					version: '3.4'

name: product_services
services:
  product_api:
    container_name: product_api
    build:
      context: .
      dockerfile: Product.API/Dockerfile
    ports:
      - "5000:8080"
    depends_on:
      - docker_sqlserver

  docker_sqlserver:
    container_name: docker_sqlserver
    build:
      context: .
      dockerfile: Product.API/db.Dockerfile
    ports:
      - "1433:1433"
				
			

Here is the explanation of this Docker Compose file:

  • Version:
    • version: ‘3.4’: Specifies the version of the Docker Compose file format being used. In this case, it’s version 3.4.
  • Project Name:
    • name: product_services: Specifies the name of the Docker Compose project as “product_services”.
  • Services:
    • product_api:
      • container_name: product_api: Sets the name of the container for the product_api service to “product_api”.
      • build: Specifies how to build the Docker image for this service.
        • context: .: Specifies the build context as the current directory (where the Docker Compose file is located).
        • dockerfile: Product.API/Dockerfile: Specifies the Dockerfile to use for building the image. It’s expected to be located at Product.API/Dockerfile relative to the context directory.
      • ports: Maps ports between the host and container.
        • “5000:8080”: Maps port 8080 from the container to port 5000 on the host. This means that the service inside the container is expected to listen on port 8080, and external clients can access it through port 5000 on the host.
      • depends_on: Specifies the dependency of this service on another service.
        • – docker_sqlserver: Specifies that this service depends on the docker_sqlserver service, meaning that the product_api service will start only after the docker_sqlserver service is up and running.
    • docker_sqlserver:
      • container_name: docker_sqlserver: Sets the name of the container for the docker_sqlserver service to “docker_sqlserver”.
      • build: Specifies how to build the Docker image for this service.
        • context: .: Specifies the build context as the current directory (where the Docker Compose file is located).
        • dockerfile: Product.API/db.Dockerfile: Specifies the Dockerfile to use for building the image. It’s expected to be located at Product.API/db.Dockerfile relative to the context directory.
      • ports: Maps ports between the host and container.
        • “1433:1433”: Maps port 1433 from the container to port 1433 on the host. This is the default port used by SQL Server. External clients can access SQL Server through port 1433 on the host.

 

This Docker Compose file creates a development environment where an ASP.NET Core API (product_api) communicates with a SQL Server instance (docker_sqlserver) using Docker containers. It ensures that the SQL Server container starts before the API container to satisfy dependencies.

As our SQL server will run inside the docker container make sure you have updated connectionStrings from app.Settings.json, the server name will be your docker sqlserver container name and the database and password should be the same as that you provide in your db.DockerFile as environment variable.

				
					"ConnectionStrings": {
  "DefaultConnection": "Server=docker_sqlserver, 1433; Database=product_db; User Id=sa; Password=myPassword1!; TrustServerCertificate=True;"
}
				
			

Step 6: Build and Run Docker Containers

Before starting let’s check if any container is running in our docker by using the docker ps command. You can see in the image below that no container is running.

Figure: visualize running containers in console

Now, build and run your Docker containers using Docker Compose.
‘Docker-compose up –build’ This command will build the Docker images for your ASP.NET Core application and SQL Server database, create containers, and start them.
In the below image, our containers have started building.

Figure: visualize docker compose up

Finally, our build is complete, let’s check it by using the command ‘docker ps’

Figure: visualize docker ps

Yes, Our containers are running. If you open the docker desktop application there also you can see those containers running.

Figure: visualize docker containers in Docker desktop

Step 7: Test your Application:

Once the containers are up and running, you can test your ASP.NET Core Web API endpoints using Postman or any other API testing too, or even by the default Product.API.http that is generated by default from Visual Studio. Use the following endpoints:

  • GET http://localhost:5000/api/products – Retrieve an alive message
  • GET http://localhost:5000/api/products/{id} – Retrieve an item by its ID.

Let’s check our results from Product.API.http from Visual Studio.

Figure: Check results from Product.API.http (1)

Figure: Check results using curl (2)

Figure: Check results using curl (2)

Figure: Check results from Product.API.http (3)

Don’t get it because there isn’t any data by ID 5 in our database

Let’s check from the terminal using curl

Figure: Check results using curl (1)

Figure: Check results using curl (2)

Figure: Check results using curl (3)

Yes, Getting the expected result.

Conclusion

Congratulations! You’ve successfully Dockerized your ASP.NET Core application along with SQL Server using Docker Compose. By creating a controller for testing and seeding data into the database, you can easily verify the functionality of your application within Docker containers.

Containerization offers numerous benefits such as consistency, scalability, and portability, making it an ideal choice for modern application deployment.

At Vivasoft Limited, we are committed to helping developers streamline their development and deployment workflows with cutting-edge technologies like Docker and Docker Compose.

Feel free to experiment further with Docker and Docker Compose to optimize your development and deployment workflow. Here I am providing some reference links for studying more about this and also sharing my source code. if you need to check any code.

Source code: https://github.com/basharovi/Product.API 

Reference : 

Potential Developer
Tech Stack
0 +
Accelerate Your Software Development Potential with Us
With our innovative solutions and dedicated expertise, success is a guaranteed outcome. Let's accelerate together towards your goals and beyond.
Blogs You May Love

Don’t let understaffing hold you back. Maximize your team’s performance and reach your business goals with the best IT Staff Augmentation