Fix #4575: project mt_created_at onto MapTo'd member in v9#4577
Merged
Conversation
The closed-shape rewrite (#4498) ported every other metadata column from the JasperFx.CodeGeneration path into ClosedShape but missed wiring the mt_created_at read-back. Result: in v9.x, `m.CreatedAt.MapTo(d => d.X)` enables the column and selects it from the DB, but no binder copies the value onto the mapped member, so `X` stays at `default(DateTimeOffset)` after a load. Sibling configurations (`LastModified.MapTo`, `Revision.MapTo`) still work because their binders were ported. This adds the missing `DocumentCreatedAtBinder<TDoc>` and wires it into `DocumentStorageDescriptorBuilder` after the LastModified slot — mt_created_at sits between mt_last_modified and mt_dotnet_type in the table (DocumentTable line 51) and mt_dotnet_type is NOT ISelectableColumn, so in the SELECT projection the column lands immediately after mt_last_modified. The binder is **read-only** — `CreatedAtColumn` carries a `transaction_timestamp()` DEFAULT for insert, and the value is immutable after that, so adding it to writeBinders would put it into the UPDATE SET list and clobber the original creation time on every subsequent save. Regression test in `flexible_document_metadata.cs`: both the populate- on-load path and the immutable-across-update invariant are covered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 29, 2026
Merged
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.
Closes #4575.
Bug
In Marten 9.x,
m.CreatedAt.MapTo(d => d.CreatedOn)configures the column but the mapped property stays atdefault(DateTimeOffset)after a load:Worked in v8, regressed in v9.
Cause
The closed-shape storage rewrite (#4498 — "Scrub all remaining JasperFx.CodeGeneration usages from Marten") ported every metadata column from the codegen path into
DocumentStorageDescriptorBuilderexcept CreatedAt. The column still getsEnabled = trueand is selected from the DB (CreatedAtColumn.ShouldSelectreturnsMember != null), but no binder copies the value from the reader to the mapped member after deserialization.Confirmed via diff inspection:
DocumentStorageDescriptorBuilderwiresTenantId,LastModified,CorrelationId,CausationId,LastModifiedBy,Headers, duplicated fields, and soft-delete; CreatedAt is the onlyISelectableColumnmetadata column with no binder.Fix
Add
DocumentCreatedAtBinder<TDoc>(mirroringDocumentLastModifiedBinder/DocumentTenantIdBinder) and add it toreadBindersright after the LastModified slot —mt_created_atsits betweenmt_last_modifiedandmt_dotnet_typein the table (DocumentTable.cs:51);mt_dotnet_typeis notISelectableColumn, so in the SELECT projection the column lands immediately aftermt_last_modified.The binder is read-only, deliberately not added to
writeBinders:CreatedAtColumncarries atransaction_timestamp()DEFAULT, so PostgreSQL fills the value on insert.writeBinderswould put the column into the UPDATE SET list and clobber the original creation time on every subsequent save.Test
when_mapping_to_the_created_at_metadataadded toflexible_document_metadata.cs— uses the existingFlexibleDocumentMetadataContextfixture. Two new cases:created_at_is_populated_on_query_only— the primary regression: confirms the mapped member is non-default after reload.created_at_is_immutable_across_updates— pins the read-only invariant so a future change adding CreatedAt towriteBinderswould fail loudly.Both pass locally (10/10 in the new fixture, including the 8 inherited from the base context).
🤖 Generated with Claude Code