.NET application modernization¶
Role¶
Act as a Senior Cloud Architect and .NET Developer. Your goal is to:
- Port a legacy .NET 5 application to .NET 8 (Core).
- Refactor an existing .NET application into a containerized application suitable for Google Cloud Run.
- Refactor the application's data layer from SQL Server to PostgreSQL.
- You MUST NOT add, remove, or change business logic. Create a faithful, 1:1 replication of features, API contracts, and UI behavior.
- You MUST explain every action you take.
Context¶
I will provide:
- The .NET source code for the application.
Instructions¶
1. Code Refactoring (Application Layer)¶
Refactor the .NET logic into a production-ready web service.
-
.NET refactoring:
- Analyze the existing codebase to understand its structure and dependencies.
- Convert the project SDK to .NET 8, convert
web.configtoappsettings.json, and handle the dependency injection changes (Program.csandStartup.cs). - Convert the
.csprojto the modern SDK-style format targeting .NET 8. - Refactor Global.asax.cs logic into the modern
Program.csmiddleware pipeline and DI container. - EF Migration: Replace
System.Data.Entity(EF6) withMicrosoft.EntityFrameworkCore. - Use Entity Framework Core, and not Dapper.
- PostgreSQL Provider: Use
Npgsql.EntityFrameworkCore.PostgreSQL. - Schema initialization:
- Analyze existing
DropCreateDatabaseIfModelChangesorIDatabaseInitializerclasses. - Refactor these into a dedicated DbInitializer class that utilizes context.Database.EnsureCreated() or applies migrations at startup (if appropriate for a demo) or generates a SQL migration script.
- Seeding: Refactor any Seed methods to work with the EF Core ModelBuilder or a custom seeding service ensuring data is populated if the DB is empty.
- Analyze existing
- Do not delete DAL files unless absolutely necessary; refactor them to maintain class structure where possible to minimize logic drift.
- To keep the structure of the application as close as possible to the application, you MUST refactor existing source code files, rather then deleting and creating new ones, whenever possible.
- You MUST NOT delete any needed functional component when migrating to EF
Core. If you encounter any issue due to using the old Entity framework
(such as
System.Data.Entityimports), you MUST refactor those classes, instead of deleting them, if possible.
-
Cloud Run Container Contract Compliance (https://docs.cloud.google.com/run/docs/container-contract):
- Port Binding: The application must listen on 0.0.0.0 on the port defined by the PORT environment variable (defaulting to 8080 if undefined).
- Logging:
- Remove any file-based logging (e.g., Log4Net file appenders).
- Configure ILogger to write structured JSON logs to Console (stdout) for Cloud Logging ingestion.
-
Concurrency & Safety:
- Identify any usage of
HttpRuntime.Cacheor static lists. Replace them withIMemoryCacheand add comments warning about scalability issues in a stateless serverless environment. - Implement a signal handler for
SIGTERMto allow graceful shutdown.
- Identify any usage of
-
Running tools:
- You MUST NOT run
dotnettools on the host. Run them in a Docker container, if necessary. - Whenever you run a Docker container, add the
--rmoption to thedocker runcommand.
- You MUST NOT run
2. Dockerfile Generation¶
Write a Dockerfile following these best practices:
- Base Image:
- Multi-Stage Build: Use the official
mcr.microsoft.com/dotnet/sdk:8.0for building andmcr.microsoft.com/dotnet/aspnet:8.0for the runtime.
- Multi-Stage Build: Use the official
- Optimization: Clean up caches and apt-get lists to minimize image size.
- Build checks: Ensure that the Dockerfile passes all docker build checks.
Read about Docker build checks
(https://docs.docker.com/reference/build-checks/) and ensure that the
Dockerfile passes all the checks. Here are the instructions to run Docker
build checks without actually building the image:
https://docs.docker.com/build/checks/#check-a-build-without-building. Run
Docker build checks without building the image by running:
docker build --check .
3. Docker Compose generation¶
Write a Docker Compose file named compose.yaml to test the refactored
application locally. The compose file should:
- Run a PostgreSQL instance for the application to use. Do not create the database, because the .NET application will create it.
- Run the .NET application
- Not include a
versionattribute because newer Compose Specs consider theversionattribute as deprecated. - Not include a definition for the
POSTGRES_DBenvironment variable in the service definition that runs the PostgreSQL database container, so the .NET application will trigger the database initialization. - Include a healthcheck for the PostgreSQL container to check if the database is
ready before starting the .NET application container. You MUST NOT depend on
the
contosouniversitydatabase being present when you build the healthcheck. Use thepg_isreadycommand to build the healthcheck. - Pass the connection string to the app service using environment variables.
4. Documentation¶
Create a modernization report in the MODERNIZATION.md file with:
- Modernization Notes: A summary of changes, specifically pointing out the
MockContextimplementation. - Local Testing: Specific
docker build,docker run, anddocker composecommands.- Ensure that all
docker runcommands have the--rmflag. - Add the
--detachand the--buildoptions to thedocker compose upcommand. - Explain how to run
docker compose logsto get logs of the running application and database.
- Ensure that all
- Endpoints: A table containing all the HTTP endpoints that the application supports.
5. Build the container image¶
Try building the container image by running the docker build command you wrote
in the MODERNIZATION.md file.
Fix any error you encounter when building the container image.
6. Try running the .NET application container¶
Try running the .NET application container by doing the following:
- Run the Docker Compose application:
docker compose up --build --detach -
Get Docker Compose logs:
- Run the
docker compose logscommand. - If the output of the the
docker compose logscommand is the same as the last time you ran it, wait for 15 secondos. - Run the
docker compose logscommand again. - If the output of the the
docker compose logscommand is the same as the last time you ran it, stop the Docker Compose application by runningdocker compose stop
- Run the
-
If Docker Compose exits with an error, examine the error. Fix any error you encounter when running the application. If you see an error connecting to
contosouniversitydatabase, but the application starts successfully, ignore this error. This is a test that .NET uses to check if it needs to create the database or not. - If you edit the
Dockerfileor the .NET application source, rebuild the container image by running thedocker buildcommand you wrote in theMODERNIZATION.mdfile. - After each run, either successful or unsuccessful, run the following
commands:
- Stop Docker Compose:
docker compose stop - Remove containers:
docker compose rm --force
- Stop Docker Compose:
Show the output of the commands you run.
7. Try sending HTTP requests¶
Test HTTP endpoints the .NET application:
- List all the HTTP endpoints not just the GET endpoints that the application supports and show me the output.
- Run the
docker compose up --build --detachcommand. - Wait for the .NET application to initialize the database.
- You MUST test ALL the HTTP endpoints (e.g. GET, POST, DELETE), not just the
GET endpoints. For each HTTP endpoint, do the following:
- Send a properly crafted request. Load anti-forgery tokens and cookies as if needed, and store them in text files.
- Get Docker Compose logs by running the
docker compose logscommand. - If you encounter an error, stop the application and fix the error.
- If you edit the Dockerfile or any file of the .NET application, rebuild
the container image by running the
docker buildcommand you wrote in theMODERNIZATION.mdfile, and run the Docker Compose application again with thedocker compose up --build --detachcommand. - Wait for the .NET application to initialize the database.
- Test the endpoint again by sending a GET request
When you're done with testing, do the following:
- Stop Docker Compose:
docker compose stop - Remove containers:
docker compose rm --force - Add a section in
MODERNIZATION.mdthat reports on which HTTP endpoints you tested. The section should have a table that lists the endpoint, the HTTP method, the command used to test the endpoint, and the command output. If theMODERNIZATION.mdfile already contains that section, update it with the latest test report. - Delete the files you created to store cookies and anti-forgery tokens.
Show the output of the commands you run.
8. Test the .NET application in a browser¶
Perform an automated UI smoke test using the Chrome DevTools MCP tools (new_page, click, take_screenshot, etc.) to validate the frontend:
- Follow all links to views
- Exexute CRUD tests in views
- Validate pagination and sorting functionality
-
Make sure there are no missing resources that result in 404 errors, such as CSS files, JavaScript files, images, and any other media file.
-
Startup: Ensure the application is running via
docker composeusing thedocker compose up --build --detachcommand. - Asset Integrity Check:
- Navigate to the application root (e.g.,
http://localhost:8080). - Inspect the Network activity. Verify that all static resources (CSS,
JavaScript, Images) load with
200 OKstatus codes. - Critical: If you detect
404 Not Foundfor assets (common in .NET migrations), fix the static file middleware configuration inProgram.csor adjust theDockerfilecopy paths.
- Navigate to the application root (e.g.,
- Functional Crawl: 4. Navigation: Identify and follow links to all major views. 5. Data Grids: If a table is present, test Pagination (next/prev pages) and Sorting (click column headers) to ensure EF Core translation is correct. 6. CRUD Operations: Perform a full Create, Read, Update, and Delete cycle on a main entity (e.g., Students/Courses) to verify the PostgreSQL data layer.
- Console Health: Check the Browser Console for any JavaScript errors and report or fix them.