Fix EF Core schema mapping for IsRowVersion()/xmin (#290) and ComplexProperty/ComplexCollection ToJson (#291)#295
Merged
jeremydmiller merged 1 commit intoMay 24, 2026
Conversation
…Property/ComplexCollection ToJson (#291) Two gaps in Weasel.EntityFrameworkCore's metadata → table mapping (DbContextExtensions.MapToTable), both surfacing as failed/incomplete migrations on EF Core 9/10 + Npgsql. ## #290 — Npgsql IsRowVersion() → xmin EF maps an Npgsql uint concurrency token (Property(x => x.Version) .IsRowVersion()) to PostgreSQL's implicit "xmin" system column rather than a new column. The mapper emitted it as a real column, so migration failed with "42701: column name 'xmin' conflicts with a system column name". Fix: add a provider hook Migrator.IsSystemColumn(string) (default false), overridden in PostgresqlMigrator to recognize PG's reserved system columns (tableoid/xmin/cmin/xmax/cmax/ctid — oid is excluded, it's a valid user column on PG 12+). MapToTable's column loop skips any property whose resolved column name is a system column. ## #291 — ComplexProperty/ComplexCollection .ToJson() OwnsOne(...).ToJson() worked (PR #233) because owned entities surface as navigations to a JSON-mapped entity type. EF Core 10's complex properties / collections are not navigations and carry no separate entity type — their JSON container lives on the IComplexType — so they were silently skipped, and the first insert failed with 42703 (column does not exist). Fix: add mapComplexJsonColumns, iterating entityType.GetComplexProperties() and emitting one container column per top-level JSON-mapped complex type (GetContainerColumnName / GetContainerColumnType, default jsonb; nullability from IComplexProperty.IsNullable). Both single (ComplexProperty) and collection (ComplexCollection) shapes serialize into a single container column. Gated #if NET10_0_OR_GREATER since complex-type JSON mapping is an EF Core 10 feature; the net9.0 target is unaffected. ## Tests - PostgresqlMigratorSystemColumnTests — 11 assertions on IsSystemColumn (system columns incl. case-insensitive; ordinary columns incl. oid). - row_version_system_column — confirms Npgsql still maps Version→xmin, then that MapToTable omits xmin while keeping id/name. - complex_json_columns (NET10) — ComplexProperty/ComplexCollection .ToJson() each produce a jsonb container column. Model-mapping tests; build the EF model offline, no live DB needed. Existing OwnsOne().ToJson() mapping tests unchanged (2/2). Weasel.Core, PostgreSQL and EFCore build clean; AOT smoke gate stays green (IsSystemColumn is a trivial virtual, no reflection). Closes #290. Closes #291. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 24, 2026
First patch release on the 9.0 line. Ships: - #294 (closes #293) — retry transient catalog-concurrency errors (XX000 "tuple concurrently updated", 40001, 40P01) in PostgreSQL migration DDL - #295 (closes #290, #291) — EF Core schema mapping: skip Npgsql IsRowVersion()->xmin system column; emit ComplexProperty/ComplexCollection .ToJson() container columns (EF Core 10) - #296 — JasperFx 2.0.0 -> 2.0.1 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related gaps in
Weasel.EntityFrameworkCore's metadata → table mapping (DbContextExtensions.MapToTable), both reported on EF Core 9/10 + Npgsql. Closes #290 and #291.#290 — Npgsql
IsRowVersion()→xminThe Npgsql-recommended optimistic-concurrency idiom maps a
uinttoken (Property(x => x.Version).IsRowVersion()) to PostgreSQL's implicitxminsystem column, not a new column. The mapper emittedxminas a real column, so migration failed:Fix: new provider hook
Migrator.IsSystemColumn(string)(defaultfalse), overridden inPostgresqlMigratorto recognise PG's reserved system columns —tableoid,xmin,cmin,xmax,cmax,ctid. (oidis intentionally excluded — it's a valid user column on PG 12+.)MapToTable's column loop skips any property whose resolved column name is a system column.#291 —
ComplexProperty(...).ToJson()/ComplexCollection(...).ToJson()OwnsOne(...).ToJson()worked (PR #233) because owned entities surface as navigations to a JSON-mapped entity type. EF Core 10's complex properties/collections are not navigations and have no separate entity type — their JSON container lives on theIComplexType— so they were silently skipped and the first insert failed with42703: column "X" does not exist.Fix: new
mapComplexJsonColumnsiteratingentityType.GetComplexProperties(), emitting one container column per top-level JSON-mapped complex type (GetContainerColumnName/GetContainerColumnType, defaultjsonb; nullability fromIComplexProperty.IsNullable). Both the single and collection shapes serialize into one container column. Gated#if NET10_0_OR_GREATER— complex-type JSON mapping is an EF Core 10 feature, so thenet9.0target is unaffected.Both fixes stay in the provider-neutral mapper + the
Migratorabstraction; no PG-specific reference leaks intoWeasel.EntityFrameworkCore.Answering the issues' questions
IsRowVersion()supported?" — yes, now; it's the standard PG concurrency idiom and Weasel skips thexminmapping correctly.ComplexProperty/ComplexCollection.ToJson()supported, or isOwnsOne().ToJson()canonical?" — they're supported now (EF 10);OwnsOnecontinues to work too.Test plan
PostgresqlMigratorSystemColumnTests— 11 assertions onIsSystemColumn(system columns incl. case-insensitive; ordinary columns incl.oid)row_version_system_column— confirms Npgsql mapsVersion→xmin, then thatMapToTableomitsxminwhile keepingid/namecomplex_json_columns(net10.0) —ComplexProperty/ComplexCollection.ToJson()each produce ajsonbcontainer columnOwnsOne().ToJson()mapping tests unchanged (2/2)🤖 Generated with Claude Code
Closes #290.
Closes #291.