Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"forwardPorts": [5000, 27017],
"portsAttributes": {
"5000": {
"label": "ASP.NET API"
"label": "ASP.NET API",
"visibility": "public"
},
"27017": {
"label": "MongoDB Atlas Local"
Expand All @@ -27,5 +28,5 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"postCreateCommand": "dotnet restore",
"postStartCommand": "echo 'Startup seed runs when the API starts (SEED_ON_STARTUP=true by default).'"
"postStartCommand": "echo 'Startup seed runs when the API starts (StartupBehaviorSettings:SeedOnStartup=true by default).'"
}
2 changes: 1 addition & 1 deletion .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
app:
image: mcr.microsoft.com/devcontainers/dotnet:1-10.0-bookworm
"image": "mcr.microsoft.com/devcontainers/dotnet:dev-10.0"
volumes:
- ..:/workspaces/mongodb-dotnet-example:cached
command: sleep infinity
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
- name: Wait for health
run: |
for i in {1..45}; do
if curl -fsS http://localhost:5050/healthz >/dev/null; then
if curl -fsS http://localhost:5050/health >/dev/null; then
exit 0
fi
sleep 2
Expand Down
18 changes: 9 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ If port 5000 is already in use, stop the conflicting process or pass a different
After any change that affects runtime behavior (`Program.cs`, `Startup.cs`, `Controllers`, `Services`, or `Models`), start the server and run the two curl commands below. Both must return HTTP 200 before considering the task complete.

```bash
curl http://localhost:5000/healthz
curl http://localhost:5000/health
curl http://localhost:5000/api/games
```

If the server fails to connect to MongoDB, verify the `.devcontainer` Atlas Local instance is running and that `GamesDatabaseSettings__ConnectionString` is set. Do not modify application code to bypass the connection failure.
If the server fails to connect to MongoDB, verify the `.devcontainer` Atlas Local instance is running and that `GamesDatabaseSettings:ConnectionString` is set in app settings. Do not modify application code to bypass the connection failure.

## Style

Expand All @@ -40,17 +40,17 @@ Run `dotnet format` before completing any C# code change.
- `.github/workflows/ci.yml`: build and integration smoke checks
- `EDD.md`: MongoDB data model contract

## Environment Variables And Configuration
## Configuration

Required configuration (via environment variables or appsettings):
Primary configuration (app settings):

- `GamesDatabaseSettings__ConnectionString` (example: `mongodb://localhost:27017`)
- `GamesDatabaseSettings__DatabaseName` (example: `GamesDB`)
- `GamesDatabaseSettings__GamesCollectionName` (example: `Games`)
- `GamesDatabaseSettings:ConnectionString` (example: `mongodb://localhost:27017`)
- `GamesDatabaseSettings:DatabaseName` (example: `GamesDB`)
- `GamesDatabaseSettings:GamesCollectionName` (example: `Games`)
- `StartupBehaviorSettings:SeedOnStartup` (`true` by default; set `false` to disable startup seeding)

Optional:
Optional runtime override:

- `SEED_ON_STARTUP` (`true` by default; set `false` to disable startup seeding)
- `ASPNETCORE_ENVIRONMENT` (`Development`, `Production`, etc.)

## MongoDB Skills
Expand Down
2 changes: 1 addition & 1 deletion EDD.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Collection: `Games`

### Seed Behavior

- On API startup, if `SEED_ON_STARTUP` is not `false`, the app checks collection emptiness
- On API startup, if `StartupBehaviorSettings:SeedOnStartup` is `true`, the app checks collection emptiness
- If empty, inserts default records from `Models/GameSeedData.cs`
- Seed operation is idempotent by emptiness guard

Expand Down
7 changes: 7 additions & 0 deletions Models/StartupBehaviorSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace mongodb_dotnet_example.Models
{
public class StartupBehaviorSettings
{
public bool SeedOnStartup { get; set; } = true;
}
}
32 changes: 18 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ dotnet run --urls http://localhost:5000
3. Verify service health and seeded data:

```bash
curl http://localhost:5000/healthz
curl http://localhost:5000/health
curl http://localhost:5000/api/games
```

Expand All @@ -77,19 +77,20 @@ dotnet run --urls http://0.0.0.0:5000

4. Open:
- Swagger UI: `http://localhost:5000/swagger/index.html`
- Health endpoint: `http://localhost:5000/healthz`
- Health endpoint: `http://localhost:5000/health`

## Environment Variables
## App Settings

The app reads nested configuration via ASP.NET Core environment binding.
Application behavior is configured through `appsettings.json` and `appsettings.Development.json`.

| Name | Required | Example | Description |
| Section | Key | Default | Description |
|---|---|---|---|
| `GamesDatabaseSettings__ConnectionString` | Yes | `mongodb://localhost:27017` | MongoDB connection string |
| `GamesDatabaseSettings__DatabaseName` | Yes | `GamesDB` | MongoDB database name |
| `GamesDatabaseSettings__GamesCollectionName` | Yes | `Games` | MongoDB collection name |
| `SEED_ON_STARTUP` | No | `true` | Inserts default games if collection is empty (`false` disables) |
| `ASPNETCORE_ENVIRONMENT` | No | `Development` | ASP.NET Core environment |
| `GamesDatabaseSettings` | `ConnectionString` | `mongodb://localhost:27017` | MongoDB connection string |
| `GamesDatabaseSettings` | `DatabaseName` | `GamesDB` | MongoDB database name |
| `GamesDatabaseSettings` | `GamesCollectionName` | `Games` | MongoDB collection name |
| `StartupBehaviorSettings` | `SeedOnStartup` | `true` | Inserts default games when collection is empty |

Environment variables are still supported as optional ASP.NET Core overrides (for example in CI/CD), but the app no longer depends on direct environment-variable reads.

## MongoDB Features Demonstrated

Expand All @@ -111,7 +112,7 @@ Relevant docs:
| `POST` | `/api/games` | Create a game |
| `PUT` | `/api/games/{id}` | Replace an existing game |
| `DELETE` | `/api/games/{id}` | Delete a game |
| `GET` | `/healthz` | Health check endpoint |
| `GET` | `/health` | Health check endpoint |

## Project Structure

Expand Down Expand Up @@ -143,19 +144,22 @@ dotnet build
## Troubleshooting

1. API starts but requests fail with MongoDB connection errors:
- Ensure MongoDB is running and reachable at `GamesDatabaseSettings__ConnectionString`.
- Ensure MongoDB is running and reachable at `GamesDatabaseSettings:ConnectionString` in app settings.

2. Port binding errors on `5000`:
- Run with a different port: `dotnet run --urls http://localhost:5050`.

3. Empty response from `/api/games` after first run:
- Confirm `SEED_ON_STARTUP` is not set to `false`.
- Confirm `StartupBehaviorSettings:SeedOnStartup` is `true`.

4. Dev container builds but API cannot reach MongoDB:
- In shared network mode, use `mongodb://localhost:27017` (not `mongodb://mongodb:27017`).

5. Swagger does not load:
- Check `/healthz` first; if healthy, verify `http://localhost:5000/swagger` and inspect server logs.
- Check `/health` first; if healthy, verify `http://localhost:5000/swagger` and inspect server logs.

6. Receiving 502 error when visiting /health or /swagger from running in GitHub Codespaces
- Ensure that port 5000 is set to public visibility in the Ports tab inside Codespaces.

## Additional Resources

Expand Down
14 changes: 7 additions & 7 deletions Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
services.Configure<GamesDatabaseSettings>(Configuration.GetSection(nameof(GamesDatabaseSettings)));
services.Configure<StartupBehaviorSettings>(Configuration.GetSection(nameof(StartupBehaviorSettings)));

services.AddSingleton<IGamesDatabaseSettings>(sp => sp.GetRequiredService<IOptions<GamesDatabaseSettings>>().Value);

Expand All @@ -44,13 +45,13 @@ public void ConfigureServices(IServiceCollection services)
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<StartupBehaviorSettings> startupBehaviorOptions)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();


}

app.UseSwagger();
Expand All @@ -65,17 +66,16 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseAuthorization();

var seedOnStartup = Environment.GetEnvironmentVariable("SEED_ON_STARTUP");
if (!string.Equals(seedOnStartup, "false", StringComparison.OrdinalIgnoreCase))
if (startupBehaviorOptions.Value.SeedOnStartup)
{
var gamesService = app.ApplicationServices.GetRequiredService<IGamesService>();
gamesService.SeedIfEmpty(GameSeedData.DefaultGames);
}

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/healthz");
endpoints.MapControllers();
endpoints.MapHealthChecks("/health");
});
}
}
Expand Down
3 changes: 3 additions & 0 deletions appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "GamesDB"
},
"StartupBehaviorSettings": {
"SeedOnStartup": true
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
3 changes: 3 additions & 0 deletions appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "GamesDB"
},
"StartupBehaviorSettings": {
"SeedOnStartup": true
},
"Logging": {
"LogLevel": {
"Default": "Information",
Expand Down
87 changes: 53 additions & 34 deletions mongodb-dotnet-example.sln
Original file line number Diff line number Diff line change
@@ -1,34 +1,53 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mongodb-dotnet-example", "mongodb-dotnet-example.csproj", "{647E6B76-A5E1-4512-AD1F-81714328BADC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x64.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x64.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x86.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x86.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|Any CPU.Build.0 = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x64.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x64.Build.0 = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x86.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mongodb-dotnet-example", "mongodb-dotnet-example.csproj", "{647E6B76-A5E1-4512-AD1F-81714328BADC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongodbDotnetExample.Tests", "tests\MongodbDotnetExample.Tests\MongodbDotnetExample.Tests.csproj", "{6157DA5A-9844-42B4-A835-E37A2FF91C8F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x64.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x64.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x86.ActiveCfg = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Debug|x86.Build.0 = Debug|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|Any CPU.Build.0 = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x64.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x64.Build.0 = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x86.ActiveCfg = Release|Any CPU
{647E6B76-A5E1-4512-AD1F-81714328BADC}.Release|x86.Build.0 = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|x64.ActiveCfg = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|x64.Build.0 = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|x86.ActiveCfg = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Debug|x86.Build.0 = Debug|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|Any CPU.Build.0 = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|x64.ActiveCfg = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|x64.Build.0 = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|x86.ActiveCfg = Release|Any CPU
{6157DA5A-9844-42B4-A835-E37A2FF91C8F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{6157DA5A-9844-42B4-A835-E37A2FF91C8F} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
Loading