2
0

Use a local SQLite3 backend instead of PostgreSQL Docker container.

* The provided SQLite3 database contains the required schemas, but no data and can be reset to if required.
* Remove all references to PostgreSQL in documentation and configuration.
* Replace native sqlite3 command with a console app to remove dependency on SQLite3 installation.
This commit is contained in:
2025-11-03 15:09:09 +01:00
parent b61e6a6cc7
commit f1c0021ad3
14 changed files with 124 additions and 139 deletions

View File

@@ -1,4 +0,0 @@
Dockerfile
docker-compose.yaml
[b|B]in
[O|o]bj

View File

@@ -1,8 +0,0 @@
# Normally, we would use a multi-stage build, but for simplicity, we are using a single stage here.
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore \
&& dotnet publish -c release -o /app
WORKDIR /app
ENTRYPOINT ["dotnet", "service.dll"]

View File

@@ -7,7 +7,7 @@ This exercise is designed to test your skills in C# and .NET.
* .NET 8.0 / C#
* Minimal ASP.NET Core Web API
* Entity Framework Core
* PostgreSQL Database (Docker)
* SQLite3 Database (simplified for portability)
* HttpClient for downstream REST API calls
* ???
@@ -49,15 +49,11 @@ sequenceDiagram
**Success Criteria: Verify that the post has been saved with the database with:**
```bash
$ docker compose exec --env PGPASSWORD=password db psql -h localhost -U user --db mydatabase -c "select * from posts;"
$ dotnet run --project show-posts/
id | user_id | title | body | updated_at
----+---------+----------------------------------------------------------------------------+-----------------------------------------------------+------------------------
1 | 1 | sunt aut facere repellat provident occaecati excepturi optio reprehenderit | quia et suscipit +| 1970-01-01 00:00:00+00
| | | suscipit recusandae consequuntur expedita et cum +|
| | | reprehenderit molestiae ut ut quas totam +|
| | | nostrum rerum est autem sunt rem eveniet architecto |
(1 row)
| Id | UserId | Title | Body | UpdatedAt |
| -- | ------ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| 1 | 1 | sunt aut facere repellat provident occaecati excepturi optio reprehenderit | quia et suscipit suscipit recusandae consequuntur expedita et cum reprehenderit molestiae ut ut quas totam nostrum rerum est autem sunt rem eveniet architecto | 1/1/1970 12:00:00AM +00:00 |
```
### Phase 2
@@ -105,34 +101,10 @@ $ docker compose exec --env PGPASSWORD=password db psql -h localhost -U user --d
## Troubleshooting
### Start database manually
Open a terminal at the repo root and execute
```bash
$ docker compose up -d db
[+] Running 3/3
✔ Network dotnet-interview-exercise_default Created 0.0s
✔ Volume "dotnet-interview-exercise_db_data" Created 0.0s
✔ Container dotnet-interview-exercise-db-1 Started 0.1s
```
### Cleanup database
In case something went wrong, wipe and rebuild the database
In case something went wrong or you want to start over, wipe the database by resetting the SQLite file.
```bash
$ docker compose down -v
[+] Running 3/3
✔ Container dotnet-interview-exercise-db-1 Removed. 0.2s
✔ Network dotnet-interview-exercise_default Removed. 0.2s
✔ Volume dotnet-interview-exercise_db_data Removed. 0.0s
$ docker compose up -d db
[+] Running 3/3
✔ Network dotnet-interview-exercise_default Created 0.0s
✔ Volume "dotnet-interview-exercise_db_data" Created 0.0s
✔ Container dotnet-interview-exercise-db-1 Started 0.1s
git restore service/service.db
```

View File

@@ -1,36 +0,0 @@
services:
service:
build:
context: .
dockerfile: Dockerfile
depends_on:
- db
networks:
- default
ports:
- "8080:8080"
environment:
ASPNETCORE_ENVIRONMENT: Docker
db:
image: postgres:alpine
restart: unless-stopped
networks:
- default
ports:
- "5432:5432" # Also for local development
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydatabase
volumes:
- db_data:/var/lib/postgresql/data
- ./dbschemas:/docker-entrypoint-initdb.d
networks:
default:
driver: bridge
volumes:
db_data:
driver: local

View File

@@ -1,38 +1,42 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "service", "service\service.csproj", "{8572A3DF-7A4C-4552-8B30-22D794E703A6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
docker-compose.yaml = docker-compose.yaml
Dockerfile = Dockerfile
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dbschemas", "dbschemas", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
dbschemas\01-create-posts-table.sql = dbschemas\01-create-posts-table.sql
dbschemas\02-posts-add-updated.sql = dbschemas\02-posts-add-updated.sql
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD652F00-A6DC-4579-AF4C-12EA8B5C7ECC}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "service", "service\service.csproj", "{8572A3DF-7A4C-4552-8B30-22D794E703A6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dbschemas", "dbschemas", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
dbschemas\01-create-posts-table.sql = dbschemas\01-create-posts-table.sql
dbschemas\02-posts-add-updated.sql = dbschemas\02-posts-add-updated.sql
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "show-posts", "show-posts\show-posts.csproj", "{D73F984F-F3CA-4CFD-AF1A-EDE2D688294C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8572A3DF-7A4C-4552-8B30-22D794E703A6}.Release|Any CPU.Build.0 = Release|Any CPU
{D73F984F-F3CA-4CFD-AF1A-EDE2D688294C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D73F984F-F3CA-4CFD-AF1A-EDE2D688294C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D73F984F-F3CA-4CFD-AF1A-EDE2D688294C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D73F984F-F3CA-4CFD-AF1A-EDE2D688294C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DD652F00-A6DC-4579-AF4C-12EA8B5C7ECC}
EndGlobalSection
EndGlobal

View File

@@ -21,10 +21,9 @@ services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddDbContext<AppDbContext>(optionsBuilder =>
{
// Configure the database connection string.
var connectionString = builder.Configuration.GetValue<string>("PostgresConnection");
Console.WriteLine($"Connecting to PostgreSQL database with connection string: {connectionString}");
optionsBuilder.UseNpgsql(connectionString);
var connectionString = builder.Configuration.GetValue<string>("SqliteConnection");
Console.WriteLine($"Setting up SQLite database with {connectionString}");
optionsBuilder.UseSqlite(connectionString);
});
var httpClientBuilder = services.AddHttpClient<JsonPlaceholderClient>();

View File

@@ -1,3 +0,0 @@
{
"PostgresConnection": "Server=db;Database=mydatabase;User Id=user;Password=password;"
}

View File

@@ -7,5 +7,5 @@
}
},
"AllowedHosts": "*",
"PostgresConnection": "Server=localhost;Database=mydatabase;User Id=user;Password=password;"
}
"SqliteConnection": "Data Source=service.db"
}

View File

@@ -6,15 +6,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.6.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.24" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.13" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.10.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="docker compose up -d db" />
</Target>
</Project>

BIN
service/service.db Normal file

Binary file not shown.

24
show-posts/Post.cs Normal file
View File

@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("posts")]
public class Post
{
[Key]
[Column("id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("title")]
[Required]
public string Title { get; set; } = string.Empty;
[Column("body")]
[Required]
public string Body { get; set; } = string.Empty;
[Column("updated_at")]
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.Parse("1970-01-01T00:00:00Z"); // TODO: Remove the default value in Phase 3.
}

View File

@@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
public class PostsDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var currentDir = System.IO.Directory.GetCurrentDirectory();
int showPostsIndex = currentDir.IndexOf("show-posts");
if (showPostsIndex > -1)
{
// Running from the bin directory, get the parent of "show-posts".
currentDir = currentDir.Substring(0, showPostsIndex - 1);
}
string repoRoot = currentDir;
optionsBuilder.UseSqlite($"Data Source={repoRoot}/service/service.db;Mode=ReadOnly;");
}
public DbSet<Post> Posts { get; set; }
}

4
show-posts/Program.cs Normal file
View File

@@ -0,0 +1,4 @@
using table.lib;
using var context = new PostsDbContext();
Table<Post>.Add(context.Posts.ToList()).ToConsole();

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>show_posts</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.13" />
<PackageReference Include="table.lib" Version="1.17.0" />
</ItemGroup>
</Project>