This article discussed one approach to having your ASP.NET development environment work with MySQL running in a docker container.
Source code: https://github.com/medhatelmasry/AspMySQL
It is assumed that the following installed on your computer:
- .NET 10.0
- Docker Desktop
- ‘dotnet-ef’ tool
Setting up MySQL docker container
To download a suitable MySQL image from Docker Hub and run it on your local computer, type the following command from within a terminal window:
docker run -d --name mysqldb -p 3333:3306 -e MYSQL_ROOT_PASSWORD=secret mysql:8.4
This starts a container named mysqldb that listens on port 3333 on your local computer. The root password is secret.
To ensure that the MySQL container is running, type the following from within a terminal window:
docker ps
You will see a message like the following:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4baa0ba99088 mysql:8.4 "docker-entrypoint.s…" 2 hours ago Up 48 minutes 0.0.0.0:3333->3306/tcp, [::]:3333->3306/tcp mysqldb
Creating our ASP.NET MVC App
Create an ASP.NET MVC app named AspMySQL with SQLite support by running the following terminal window commands:
dotnet new mvc --auth individual -o AspMySQL
cd AspMySQL
To run the web application and see what it looks like, enter the following command:
dotnet watch
The app starts in your default browser and looks like this:
We need a suitable MySQL Entity Framework driver. One such driver is the official MySQL driver from Oracle. Run this pair of commands to replace the SQLite driver with the Oracle MySQL driver:
dotnet remove package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package MySql.EntityFrameworkCore
Let us configure our web application so that the connection string can be constructed from environment variables. Open the Program.cs file in your favourite editor and comment out (or delete) the following statements around lines 8-10:
Replace the above code with the following:
var host = builder.Configuration["DBHOST"] ?? "localhost";
var port = builder.Configuration["DBPORT"] ?? "3333";
var password = builder.Configuration["DBPASSWORD"] ?? "secret";
var db = builder.Configuration["DBNAME"] ?? "aspnetDB";
var user = builder.Configuration["DBUSER"] ?? "root";
string connectionString = $"Server={host};Port={port};Database={db};User={user};Password={password};";
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySQL(connectionString));
Five environment variables are used in the database connection string. These are: DBHOST, DBPORT , DBPASSWORD, DBNAME and DBUSER. If these environment variables are not found then they will take on default values: localhost, 3333, secret, aspnetDB and root respectively.
Go ahead and delete the connection string from appsettings.json as it is not needed anymore:
"ConnectionStrings": {
"DefaultConnection": "DataSource=app.db;Cache=Shared"
},
Entity Framework Migrations
We can instruct our application to automatically process any outstanding Entity Framework migrations. This is done by adding the following statement to Program.cs right before the last app.Run() statement:
using (var scope = app.Services.CreateScope()) {
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
}
Since SQLite is different from MySQL, we must delete all migrations and re-create them. Therefore, follow these steps:
- delete the Data/Migrations folder.
- delete file: app.db
- create new migrations with this terminal window command:
dotnet ef migrations add M1 -o Data/Migrations
Test app
Now, let's test our web app and see whether it can talk to the containerized MySQL database server. Run the web application with the following terminal command:
dotnet watch
Click on the Register link on the top right side.
I entered an Email, Password and Confirm password, then clicked on the Register button. The website then displays the following page that requires that you confirm the email address:
Click on the “Click here to confirm your account” link. This leads you to a confirmation page:
Login with the email address and password that you registered with.
The message on the top right side confirms that the user was saved and that communication between the ASP.NET app and MySQL is working as expected.Dockeri-zing app
We will generate the release version of the application by executing the following command from a terminal window in the root directory of the web app:
dotnet publish -o distrib
The above command instructs dotnet to produce the release version of the application in the distrib directory. When you inspect the distrib directory, you will see files like the following:
The highlighted file in the above image is the main DLL file that is the entry point into the web application. Let us run the DLL. To do this, change to the distrib directory, then run your main DLL file with:
cd distrib
dotnet AspMySQL.dll
This displays the familiar messages from the web server that the app is ready to be accessed from a browser.
Hit CTRL C to stop the web server.
We now have a good idea about the ASP.NET artifacts that need to be copied into a container.
In a terminal window, stop and remove the MySQL container with:
docker rm -f mysqldb
Return to the root directory of your project by typing the following in a terminal window:
cd ..
Docker image for web app
We need to create a docker image that will contain the .NET runtime. At the time of writing this article, the current version of .NET is 10.0.
We can exclude files from being copied into the container image. Add a file named .dockerignore in the root of the web application with this content:
**/.git
**/.gitignore
**/node_modules
**/npm-debug.log
**/.DS_Store
**/bin
**/obj
**/distrib
**/.vs
**/.vscode
**/.env
**/*.user
**/*.suo
**/.idea
**/coverage
**/.nyc_output
**/docker-compose*.yml
**/Dockerfile*
**/.github
**/README.md
**/LICENSE
Create a text file named Dockerfile and add to it the following content:
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy project file and restore dependencies
COPY *.csproj .
RUN dotnet restore
# Copy source code
COPY . .
# Publish application
RUN dotnet publish -c Release -o /app/publish
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80
EXPOSE 443
# Run the application
ENTRYPOINT ["dotnet", "AspMySQL.dll"]
docker-compose.yml
We will next create a docker .yml file that orchestrates the entire system involving two containers: a MySQL database server and our web app. In the root folder of your application, create a text file named docker-compose.yml and add to it the following content:
volumes:
mysqldata:
services:
db:
image: mysql:8.4
volumes:
- mysqldata:/var/lib/mysql
#restart: always
ports:
- "3333:3306"
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_TCP_PORT: 3306
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-uroot", "-psecret" ]
interval: 5s
timeout: 5s
retries: 20
start_period: 30s
webapp:
build:
context: .
depends_on:
db:
condition: service_healthy
ports:
- "7777:80"
#restart: always
environment:
- DBHOST=db
- DBPORT=3306
- DBPASSWORD=secret
- DBNAME=snoopyDB
- DBUSER=root
Running the yml file
To find out if this all works, go to a terminal window at the root directory of the application and run the following command:
docker-compose up -d --build
Point your browser to http://localhost:7777/ and you should see the main web page. Register a user, confirm the email, and login. It should all work as expected.
Cleanup
Run the following command to shutdown docker-compose and cleanup:
docker-compose down -v
Conclusion
We have seen how straight forward and easy it is to containerize an application and its database with docker-compose.



















