mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 08:31:14 +00:00
[AZ-484] Fix multi-source tile reads: drop Dapper enum handler
Two integration-test failures uncovered after the initial commit: 1) GetTilesByRegionAsync outer ORDER BY referenced 'updated_at' but the inner DISTINCT ON subquery aliased it to 'UpdatedAt' (Postgres folds to 'updatedat'). DISTINCT ON already guarantees one row per (latitude, longitude, ...) so the third tiebreak was unreachable; removed it. 2) Dapper 2.1.35 silently bypasses SqlMapper.TypeHandler<T> for enum types during read deserialization (Dapper issue #259). The TileSourceTypeHandler worked for writes but reads fell through to Enum.TryParse, which cannot map 'google_maps' to GoogleMaps. Pivoted: TileEntity.Source is now a string (the wire value). TileSource enum stays as the public producer surface in Common.Enums; TileSourceConverter (Common.Enums) provides ToWireValue / FromWireValue / IsValidWireValue at the boundary. TileSourceTypeHandler deleted; registration removed from DapperEnumTypeHandlers.RegisterAll. tile-storage.md Inv-5 amended to document the storage choice. _docs/LESSONS.md L-001 records the Dapper bypass for future cycles. Full suite passes (213 unit + integration suite incl. AZ-484 AC-1..AC-5, security SEC-01..SEC-04, AZ-356/362/357). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# Engineering Lessons
|
||||
|
||||
Recurring bugs, surprising library behaviors, and process insights extracted from completed cycles. Newest at the top. Keep entries short — this is for fast scanning at the start of new cycles, not exhaustive history.
|
||||
|
||||
---
|
||||
|
||||
## L-001 — Dapper `TypeHandler<T>` is bypassed for enum types during read deserialization
|
||||
|
||||
**Cycle**: 1 (AZ-484)
|
||||
**Discovered by**: integration test failure (`Error parsing column 12 (source=google_maps - String)`); root-caused via web search to long-standing Dapper issue [#259](https://github.com/DapperLib/Dapper/issues/259).
|
||||
**Affects**: Dapper 2.1.35 (and most other versions until the proposed `Settings.PreferTypeHandlersForEnums` opt-in in PR #2200, not yet merged).
|
||||
|
||||
**What happens**
|
||||
Registering `SqlMapper.AddTypeHandler(new MyEnumHandler())` for an `enum` type — even via `SqlMapper.TypeHandler<TEnum>` — works for **writes** (the handler's `SetValue` is invoked for parameter binding) but is **silently bypassed for reads**. Dapper's IL-emitted deserializer checks `IsEnum` first and falls back to `Enum.TryParse(string, ignoreCase: true)`.
|
||||
|
||||
**Why this is dangerous**
|
||||
If the enum's wire string happens to match a member name case-insensitively (e.g., `RegionStatus.Failed` ↔ `"failed"`), the bypass goes unnoticed and round-trip works *accidentally*. The bug only surfaces when the wire format diverges from the C# member name (e.g., `TileSource.GoogleMaps` ↔ `"google_maps"` — `Enum.TryParse("google_maps")` does not match `GoogleMaps` because of the underscore).
|
||||
|
||||
**Recommended approach**
|
||||
- Do **not** rely on `SqlMapper.TypeHandler<TEnum>` for read-side enum mapping unless the wire values match the enum member names case-insensitively.
|
||||
- For enums whose wire format diverges (snake_case, kebab-case, custom IDs), store the entity field as `string` and provide an explicit converter (`*Converter.ToWireValue` / `FromWireValue`) for use at the service-layer boundary. This is what AZ-484 does for `TileEntity.Source` ↔ `TileSourceConverter`.
|
||||
- Unit-test the converter directly. Do not assume that round-tripping through Dapper proves anything for enums.
|
||||
|
||||
**Detection**
|
||||
- Unit tests of the type handler in isolation will pass even when the handler is bypassed at runtime.
|
||||
- Failure surfaces only at integration-test time when the actual SELECT runs.
|
||||
- If you must keep an enum-typed field, write at minimum one integration test that reads the enum back through Dapper from a real database row.
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user