dotnet / dotnet/maui-samples

Race conditions, thread-safety issues, and code quality in Developer Balance sample

Open
#748 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
3.7k
Forks
1.5k
PR merge metrics
No merged PRs in 30d

Description

### Problems
1. `ClearTables()` was declared as `async void`, meaning `LoadSeedDataAsync()` could not `await` it. This created a race condition where seed data inserts could execute before table drops completed. Any exception inside `async void` would also crash the app silently (unhandled on the thread pool), and the original error was swallowed via `Console.WriteLine`.
2. `SeedDataService.ClearTables()` called `Task.WhenAll` on all four repositories concurrently. However, `ProjectRepository.DropTableAsync()` internally already calls `_taskRepository.DropTableAsync()` and `_tagRepository.DropTableAsync()`. This caused the same tables to be dropped from two concurrent call sites simultaneously, risking SQLite locking errors (`SQLITE_BUSY`) and non-deterministic behavior.
3. The file was named `TaskRespository.cs` (note "Resp" instead of "Repo") while the class inside was correctly named `TaskRepository`. This typo made the file hard to discover and inconsistent with the rest of the codebase.
4. All four repositories used a simple `bool _hasBeenInitialized` flag to guard `Init()`. Under concurrent `async` access, multiple callers could pass the `if (_hasBeenInitialized) return;` check before any of them set the flag to `true`, resulting in multiple table creation attempts running in parallel.
5. `ListAsync()` issued separate SQL queries for each project's tags and tasks. With N projects this results in 2N+1 total queries, which is inefficient and a well-known anti-pattern.
6. The `ProjectsTags` class was defined but never referenced anywhere in the codebase. The join table is managed entirely via raw SQL in `TagRepository`. The class also had an `ID` field not present in the actual database schema (which uses a composite primary key).
7. In `ListAsync(int projectID)`, one SQL parameter was added without the `@` prefix:
8. `MainPageModel`: returned `null` when project was null → caller receives a null `Task`, which can throw `NullReferenceException` if awaited.
9. `JsonContext` was the only class in the entire template with no `namespace` declaration, making it a global-namespace type. This was inconsistent and could cause resolution ambiguity.
10. `DisplaySnackbarAsync` created a `CancellationTokenSource` that was never disposed and had no timeout, meaning it would hold resources indefinitely.

### Steps to Reproduce
1. Clone the repo
2. Run the developer balance app

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.