Repository navigation
AOT: route JsonUnicodeBodyConverter (SqlServiceBroker) through GetTypeInfo #515
Description
Goal
Give Chatter.MessageBrokers.SqlServiceBroker's JsonUnicodeBodyConverter a metadata-aware serialization path, following the pattern #509 establishes for the Chatter.MessageBrokers Body Converters.
Current state on master (verified at 7408c31)
src/Chatter.MessageBrokers.SqlServiceBroker/src/Chatter.MessageBrokers.SqlServiceBroker/JsonUnicodeBodyConverter.cs is 25 lines. Its serialization calls are:
- line 10 —
Convert<TBody>(byte[] body)returnsChatterJson.Deserialize<TBody>(Stringify(body)). - line 20 —
Stringify(object body)returnsChatterJson.Serialize(body).
It has no constructor, takes no options, and has a parameterless default. It handles a UTF-16 payload (ContentType is "application/json; charset=utf-16"), with Encoding.Unicode for the byte conversions.
Corrections to earlier notes
- The quoted code does not exist. This issue previously stated that the converter "still calls
JsonSerializer.Deserialize<TBody>(Stringify(body), ChatterJson.Options)/Serialize(body, ChatterJson.Options)directly". It does not, and it could not:ChatterJson.Optionsisinternal(src/Chatter.MessageBrokers/src/Chatter.MessageBrokers/ChatterJson.cs:24), andsrc/Chatter.MessageBrokers/src/Chatter.MessageBrokers/Properties/AssemblyInfo.csgrants productionInternalsVisibleToonly toChatter.MessageBrokers.AzureServiceBusandChatter.MessageBrokers.Reliability.Cosmos— not to this package. The converter consumes the published Wire Serialization capability, which is exactly whatsrc/Chatter.MessageBrokers/CONTEXT.mdprescribes for a sibling package. - "Hardcoded to the reflection-based default" is therefore the wrong framing. The converter is correctly coupled to the published capability; the AOT problem is that the capability itself has no metadata-aware form yet. That is the real gap, and it is shared with
Chatter.MessageBrokers.RabbitMQ'sRabbitMqBodyConverter, which has the identical shape. - AOT: route Body Converter JSON through JsonTypeInfo instead of the annotated JsonSerializer overloads #509 has not landed here. No file in the repository routes serialization through
options.GetTypeInfo. There is no recipe to apply yet — this issue is a dependent of AOT: route Body Converter JSON through JsonTypeInfo instead of the annotated JsonSerializer overloads #509, not a follow-on to a completed change. - The
RuntimeFeature.IsDynamicCodeSupportedguard pattern attributed to AOT: ChatterJson's shared options field initializer eagerly builds the reflection-based resolver #431 and AOT: route Body Converter JSON through JsonTypeInfo instead of the annotated JsonSerializer overloads #509 does not exist anywhere in the repository.
Design constraint
Adding a public JsonSerializerOptions constructor parameter would make the serializer configuration a caller-facing surface, which src/Chatter.MessageBrokers/CONTEXT.md's Wire Serialization term explicitly warns against, and it would not even give this package access to Chatter's own options across the package boundary. The likely correct shape is to extend the published capability with a metadata-aware entry point that JsonUnicodeBodyConverter and RabbitMqBodyConverter both call, rather than to publish options on each converter. Resolve that question in #509 and apply the answer here.
Scope
src/Chatter.MessageBrokers.SqlServiceBroker/src/Chatter.MessageBrokers.SqlServiceBroker/JsonUnicodeBodyConverter.cs, lines 10 and 20.- Adopt whatever metadata-aware seam AOT: route Body Converter JSON through JsonTypeInfo instead of the annotated JsonSerializer overloads #509 settles on. Do not introduce a package-local variant.
- Any new public constructor parameter is a public-API-surface and SemVer question. Flag it; do not characterize it here.
- Preserve the UTF-16 encoding behavior and byte-for-byte wire output exactly.
- Add coverage:
Chatter.MessageBrokers.SqlServiceBrokeris referenced by no AOT smoke test, because no AOT smoke-test project exists yet. AOT Phase 0: Infrastructure and red baseline #276 creates the harness and AOT: cover AzureServiceBus, SqlServiceBroker and SqlChangeFeed receiver registration in the Native AOT smoke tests #429 tracks adding this module to it. - Measure real publish-time and compile-time diagnostic counts before and after, from a clean tree.
Depends on #509 and, transitively, on #279 and #276.
Part of #275.
Activity
Greven145 commentedon Oct 1, 2026
Correction, same pattern as #510's correction. This body measures against brenpike/Chatter's master (7408c31), where none of this initiative has landed. On aotIntegration, the real branch:
#509is merged (PR Implement unit tests for Chatter.CQRS #19). It establishes the exact GetTypeInfo/JsonTypeInfo pattern this issue asks for, already applied to RabbitMqBodyConverter.ChatterJson.Optionsispublic(ChatterJson.cs:171,public static JsonSerializerOptions Options => ReflectionDefaults.Options;), not internal.InternalsVisibleTo("Chatter.MessageBrokers.SqlServiceBroker")IS granted (Properties/AssemblyInfo.cs), contradicting the body's claim..mdfiles. Checked again, same as the AOT: source-generate RoutingSlip serialization #510 correction.ChatterJson.ReflectionDefaultOrThrow()and theRuntimeFeature.IsDynamicCodeSupportedguard exist (Implement Outbox pattern #18/AOT: ChatterJson's shared options field initializer eagerly builds the reflection-based resolver #431, merged) and are already the established default-resolution pattern used by AOT: route Body Converter JSON through JsonTypeInfo instead of the annotated JsonSerializer overloads #509 and AOT: source-generate RoutingSlip serialization #510.Chatter.Aot.Smoke.Tests/).The "design constraint" section does not apply. Proceeding against aotIntegration's real state: mirror RabbitMqBodyConverter's already-merged shape directly.
Greven145 commentedon Oct 1, 2026
PR #21: Greven145#21
Mirrored RabbitMqBodyConverter's already-merged shape exactly: GetTypeInfo + JsonTypeInfo overloads, RuntimeFeature.IsDynamicCodeSupported-conditional DI registration.
Found and fixed a real bug beyond the converter itself: SqlServiceBrokerReceiver's envelope-converter fallback (new JsonUnicodeBodyConverter()) was a bare, eager construction that would throw under real Native AOT before the receiver's own try block runs, regardless of whether IBodyConverterFactory would have resolved correctly — the same "parameter added but never threaded through the real path" mistake the #510 review caught. Fixed via an optional JsonSerializerOptions on the receiver's constructor.
Verified: 214/214 tests both TFMs (3 new). Real clean rebuild of all 9 modules: trim baseline 36 → 32, exactly 4 fixed, 0 new.
Out of scope, stated in the PR: no AOT smoke-test coverage added (Chatter.Aot.Smoke.Tests doesn't reference this module at all — that's #429's job). SemVer on the two API additions flagged, not decided.
CI running on the PR.
Greven145 commentedon Oct 1, 2026
Done. PR #21. Adversarial review found the headline bug was still live (OutboundBrokeredMessage missing from ChatterMessageBrokerJsonContext, silently corrupting envelope decode instead of throwing) — reproduced and fixed. Eager-throw-at-construction tension deliberately left as-is and documented; it's #516's exact scope.
AOT: fail a missing serialization opt-in at startup, not first delive…