BrighterCommand / BrighterCommand/Brighter
JsonSerialisationOptions.Options omits IncludeFields, silently dropping public fields to default
- Dominant language
- C#
- Stars
- 2.5k
- Forks
- 296
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
### Describe the bug
`JsonSerialisationOptions.Options` (`src/Paramore.Brighter/JsonConverters/JsonSerialisationOptions.cs:25-31`) never sets `IncludeFields`, which defaults to `false` in `System.Text.Json`. Any **public field** on a serialised type is therefore silently dropped and arrives as `default`. No exception is thrown.
This surfaced while confirming the root cause of #4152. That issue's stated cause (positional `readonly record struct` failing to bind through its primary constructor) was **refuted empirically** — positional record structs round-trip correctly. But while testing, this defect reproduced end-to-end through the full Azure scheduler envelope and produces the *exact* symptom #4152 reports: a member silently arriving as `default`.
### To Reproduce
Round-trip any type with a public field through `JsonSerialisationOptions.Options`:
```csharp
public record MyRequest : Command
{
public int PlainField; // silently dropped -> 0
}
```
Verified end-to-end through the scheduler path (serialize at `AzureServiceBusScheduler.cs:83` -> envelope -> `FireAzureScheduler.MapToRequest` -> `FireSchedulerRequestHandler.cs:62`):
```
FINAL: Type=Delete Value='abc-123' PlainField=0
ActionId survived end-to-end: True
PlainField survived end-to-end: False
```
Reproduced identically on net8.0 / net9.0 / net10.0 (STJ 8.0.0.0 / 9.0.0.0 / 10.0.0.0).
### Expected behaviour
Either public fields round-trip, or the contract ("scheduled requests must use properties, not fields") is documented and ideally enforced loudly rather than failing silently.
### Why V11
The obvious fix is `IncludeFields = true`, but `JsonSerialisationOptions.Options` is a **shared static** used at every Brighter serialisation site — message bodies, outbox/inbox persistence, scheduler envelopes. Turning it on changes the wire and at-rest format for any user type carrying public fields: newly-emitted JSON gains members it did not have before, which existing consumers and stored rows were never written to expect.
That is a significant behavioural change and does not belong in a patch release. Flagging for V11 so it can be taken with a proper compatibility review, and so we can decide whether the right answer is the global flag or a narrower, documented contract for scheduled requests.
### Related
- #4152 — where this was found. Its own stated cause is refuted; this is a candidate for the behaviour that issue actually observed.
Contributor guide
Research direction
Start with src/Paramore.Brighter/JsonConverters/JsonSerialisationOptions.cs:25-31 and inspect the shared Options usage at AzureServiceBusScheduler.cs:83 and FireSchedulerRequestHandler.cs:62. Trace the envelope through FireAzureScheduler.MapToRequest, then determine whether public fields should round-trip or whether the scheduled-request contract must be documented and enforced with compatibility in mind.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100