Tuesday, September 29, 2026

docker-compose with MySQL and ASP.NET

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:

  1. .NET 10.0 
  2. Docker Desktop 
  3. ‘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:

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(connectionString));

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:

  1. delete the Data/Migrations folder.
  2. delete file: app.db
  3. 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.

The ASP.NET MVC user register page.

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.

Thursday, September 3, 2026

Enhancing C# Development with GitHub Copilot CLI and VS Code

GitHub Copilot is transforming how developers work by turning AI from a simple code generator into a practical development partner. In this article, we explore how to use GitHub Copilot CLI and Visual Studio Code together with the Awesome Copilot ecosystem to build better .NET applications, customize AI behavior, and bring domain-specific guidance directly into the development workflow. From designing models and applying best practices to installing reusable agents, instructions, and skills, this walkthrough shows how to make Copilot feel deeply integrated with the way you code.

Using GitHub Copilot CLI

Create a sample .NET Console app

From within a termnal window in a working directory, type the following commands:

dotnet new console -o AwesomeAthlete
cd AwesomeAthlete

Install GitHub Copilot CLI

Follow instructions at Installing GitHub Copilot CLI to install the GitGub Copilot CLI.

Start the copilot app by typing the following command inside the same folder as the .NET app created earlier:

copilot

Explore online plugins repo

Visit: https://github.com/github/awesome-copilot and review the plugins/csharp-dotnet-development plugin.

Install the plugin by entering this command in GitHub Copilot CLI:

/plugin install csharp-dotnet-development@awesome-copilot

To view a list of plugins, enter this command:

/plugin list

Let's get the plugin to do something useful. For example, ask the plugin to help you design some domain models by entering this command:

@csharp-dotnet-development help me design a class model for Athlete

Thereafter, you can enter this command to add the required C# classes:

add the class models to my app and put code in Program.cs that calls those classes and prints sample data to the console

Among others, the plugin contains a command named /csharp-dotnet-development:dotnet-best-practices. We can use this command by entering this instruction:

/csharp-dotnet-development:dotnet-best-practices

Since we have a very simple C# application, the only suggestion made by AI is to add documentation to the domain classes.

Exit GitHub Copilot CLI by typing in the /exit command.

/exit

Using Visual Studio Code (VS Code)

If you do not already have VS Code installed on your computer, you can get it from https://code.visualstudio.com/. Make sure to install the GitHub Copilot Chat extension before proceeding.

Open the AwesomeAthlete folder in VS Code. You ca do that by simply typing in the following from a termninal window from inside the AwesomeAthlete folder:

code .

Customize or Extend the Plugin

You can modify plugin components to suit your needs.

In plugin folder: ~/.copilot/installed-plugins/awesome-copilot/<plugin-name>/

  • Edit com.github.copilot/agents/*.md to change agent behavior
  • Edit skills/*/SKILL.md to add new skills

Restart VS Code to reload changes

Installing Awesome Copilot artifacts

Point your browser to Awesome GitHub Copilot. Let's try installing the Caveman Mode instruction that optimizes token interaction with AI. Click on Instructions.

Find the Caveman Mode instruction. Click on VS Code Install.

Click Yes on this dialog.

Choose to install the instructions in the current workspace under folder .github, instead of globally.

Accept the default name caveman-mode for the instructions.

The caveman-mode-instructions.md file is saved in your workspace under .github.

Here is an example on how to use this instruction. Enter the following into the VS Code chat window:

caveman-mode how many years did it take to build the empire state building in new york

The prompt is distilled to the essential keywords: Empire State Building construction duration. The response stays concise and focused, avoiding unnecessary wording. This keeps token usage low and saves you money.

Awesome Copilot

The Awesome Copilot (By Tim Heuer) extension for VS Code allows you to browse and download instructions, prompts, chat modes, and agents from the Awesome Copilot community.

Install the above extension into your VS Code. You wiill find Awesome Copilot in your explorer.

You will find folders Instructions, Agents, and Skills.

For example, expand the Agents node, then select CSharpExpert.agent.md.

If you decide to download this agent, click on the download tool beside it.

You can change the name of the agent .md file. Simply hit Enter to accept the default name.

The agent gets installed in your local workspace under .github/agents.

You can also modify any of these AI artifacts after you install them locally.

💡 TIP: There are new AI artifacts being added regularly. Click on refresh to load the latest.

@agentPlugins

There are multiple ways of installing AI plugins into VS Code. In your VS Code Extensions tab enter the keywoord @agentPlugins.

There are many plugins that you can choose from. You can narrow down the list by adding a filter. For example, I entered the csharp filter and received a short list of the plugings that fit that keyword:

Click on the csharp-dotnet-development plugin. You will see more information about the plugin. In this example, the plugin consists of a number of slash (/) commands and one agent.

I you are in a C# application, you can use the /csharp-dotnet-development:csharp-xunit slash command to generate test cases with this prompt:

/csharp-dotnet-development:csharp-xunit generate test cases

Plugins consist of plugin.json file, skills, agents, hooks, and MCP. Below is an illustration of the plugin architecture:

Any agents that are installed in your environment can be invoked from the chat window. For example, in the below illustration, I installed the CSharpExpert.agent.md agent locally and it is appears in the chat window ready to be invoked:

You can, at any time, disable or uninstall any plugin by right-clicking on it in the extensions tab and choosing disable or uninstall.

Conclusion

GitHub Copilot plugins extend AI-assisted development beyond simple code generation. With GitHub Copilot CLI, VS Code, and the Awesome Copilot ecosystem, you can add specialized agents, instructions, skills, hooks, and commands to your C# workflow.

These tools help you design .NET applications, apply best practices, generate tests, and customize Copilot to match your development style. Explore the available artifacts, install the ones that fit your needs, and adapt them to make GitHub Copilot a more effective development partner.

References

Manage Agents, Instructions, Prompts, & Skills in Seconds with this VS Code Extension

Get Awesome-Copilot custom chat modes and prompt files - right from within GitHub Copilot Chat.

GitHub Copilot Agent Plugins: Package & Distribute Skills, MCP, Hooks & Custom Agents