# Module: Azaion.Common.Database.AzaionDbSchemaHolder ## Purpose Static holder for the linq2db `MappingSchema` that maps C# entities to PostgreSQL table/column naming conventions and handles custom type conversions. > **Cycle 1 (2026-05-13)** — `DetectionClass` mapping added (AZ-513). > > **Cycle 2 (2026-05-14)** — `AuditEvent` and `Session` mappings added; `User.MfaRecoveryCodes` mapped as `DataType.BinaryJson` (jsonb) to satisfy Npgsql's strict OID matching for jsonb columns (AZ-534). ## Public Interface | Member | Type | Description | |--------|------|-------------| | `MappingSchema` | `static readonly MappingSchema` | Pre-built schema with column name and type mappings | ## Internal Logic Static constructor: 1. Creates a `MappingSchema` with a global callback that converts all column names to snake_case via `StringExtensions.ToSnakeCase`. 2. Uses `FluentMappingBuilder` to configure the entities: - **`User`** — table `"users"`, `Id` PK (Guid), `Role` text with `Enum.Parse` round-trip, `UserConfig` JSON via `Newtonsoft.Json` round-trip, **`MfaRecoveryCodes`** (AZ-534) as `DataType.BinaryJson` so Npgsql sends the jsonb OID instead of text (otherwise inserts fail with "column is of type jsonb but expression is of type text"). - **`DetectionClass`** — table `"detection_classes"`, `Id` PK + identity (DB-assigned). - **`AuditEvent`** (AZ-537+534) — table `"audit_events"`, `Id` PK + identity. - **`Session`** (AZ-531+535+533+534) — table `"sessions"`, `Id` PK (Guid). All other columns rely on the snake_case auto-mapping. ## Dependencies - `User`, `RoleEnum`, `DetectionClass`, `AuditEvent`, `Session` entities - `UserConfig` (for the JSON conversion) - `StringExtensions.ToSnakeCase` - linq2db `MappingSchema`, `FluentMappingBuilder` - `Newtonsoft.Json` ## Consumers - `DbFactory.LoadOptions` — passes `MappingSchema` to `DataOptions.UseMappingSchema()` for both read and write `DataOptions` (single shared instance). ## Data Models Defines the ORM mapping for `users`, `detection_classes`, `audit_events`, `sessions` tables. ## Configuration None — all mappings are compile-time. The `MappingSchema` is built once at first use of the static class and shared across the entire process. ## External Integrations None directly; mappings are used when queries execute against PostgreSQL. ## Security None. ## Tests Exercised end-to-end via the e2e suite. Misconfigured jsonb mapping would surface as a `42804` Postgres error (`column is of type jsonb but expression is of type text`) on the first MFA confirm — covered by `e2e/Azaion.E2E/Tests/MfaLoginTests.cs`.