Race conditions, thread-safety issues, and code quality in Developer Balance sample
- 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.