Skip to content

Modernize UnifiedId: IParsable/ISpanParsable/IFormattable/ISpanFormattable + System.Text.Json - #24

Merged
SergiySeletsky merged 2 commits into
masterfrom
modernize-unifiedid-interfaces-stj
Jul 13, 2026
Merged

SergiySeletsky merged 2 commits into
masterfrom
modernize-unifiedid-interfaces-stj

Conversation

@SergiySeletsky

Copy link
Copy Markdown
Owner

Summary

Makes UnifiedId a first-class modern .NET identifier and a viable backing value for strongly-typed id libraries (e.g. devlooped/StructId, Vogen, StronglyTypedId), by adding the parsing/formatting interfaces those libraries and the BCL rely on, plus a System.Text.Json converter.

Everything here is additive and non-breaking (new interfaces + a new package; no existing signature/behavior changes).

What changed

  • Multi-target the core package: netstandard2.0netstandard2.0;net8.0;net10.0. netstandard2.0 keeps its existing zero-dependency char[] code path unchanged.
  • Modern interfaces on net8.0+ (guarded by #if NET8_0_OR_GREATER): IParsable<UnifiedId>, ISpanParsable<UnifiedId>, IFormattable, ISpanFormattable.
    • Hot paths (TryParse/TryFormat) are allocation-free (Span/stackalloc); the string Parse overloads only allocate on the rare error path.
    • format/provider are accepted but ignored — UnifiedId has a single canonical 13-char HEX32 representation. Empty/round-trip semantics preserved exactly.
  • Nullability alignment: Equals(object?), Equals(string?), CompareTo(object?), CompareTo(string?) now match the BCL interface contracts. These mismatches were latent and only surface once the modern (annotated) reference assemblies are targeted; the change is metadata-only.
  • New Unified.Text.Json package (mirrors the existing Unified.Json/Newtonsoft package): UnifiedIdJsonConverter : JsonConverter<UnifiedId> serializing the canonical HEX32 string, including WriteAsPropertyName/ReadAsPropertyName so a UnifiedId works as a dictionary key.

Testing

  • 12 new tests (ModernInterfacesTests, SystemTextJsonTests) — including a generic where T : IParsable<T> call proving the constraint is satisfied, span/too-small-buffer TryFormat, interpolation via ISpanFormattable, and STJ round-trip + dictionary-key round-trip.
  • 42 tests total, all green. Full solution builds clean (0 warnings, TreatWarningsAsErrors on) across netstandard2.0/net8.0/net10.0.
  • dotnet pack verified: Unified ships lib/netstandard2.0 + lib/net8.0 + lib/net10.0; Unified.Text.Json declares its Unified + System.Text.Json deps.

Intentionally out of scope (follow-ups)

  • implicitexplicit conversion tightening (breaking; belongs to a major version).
  • EF Core ValueConverter + ValueComparer, Dapper TypeHandler, TypeConverter.
  • Retiring the legacy ISerializable path (kept as-is; SYSLIB0050 suppressed on modern targets, matching the test project's existing convention).

🤖 Generated with Claude Code

SergiySeletsky and others added 2 commits July 12, 2026 19:26
…rmattable + System.Text.Json

Multi-target the core package (netstandard2.0;net8.0;net10.0) and implement the
modern .NET parsing/formatting interfaces on net8.0+ so UnifiedId participates in
generic parsing, ASP.NET Core model binding, allocation-free Span formatting, and
can serve as the backing value for strongly-typed id libraries (e.g. StructId).

- UnifiedId implements IParsable<UnifiedId>, ISpanParsable<UnifiedId>, IFormattable
  and ISpanFormattable (guarded by #if NET8_0_OR_GREATER). Hot paths
  (TryParse/TryFormat) are allocation-free; format/provider are accepted but
  ignored (single canonical 13-char HEX32 representation).
- Align Equals/CompareTo(object?/string?) parameter nullability with the BCL
  interface contracts (metadata-only; surfaced by the net8+ targets).
- New Unified.Text.Json package: UnifiedIdJsonConverter (JsonConverter<UnifiedId>)
  serializing the canonical HEX32 string, including dictionary-key support.
- Tests: 12 new (modern interfaces + System.Text.Json); 42 total, all green.

All changes are additive and non-breaking. Legacy ISerializable is retained
(SYSLIB0050 suppressed); implicit->explicit operator tightening and
EF Core/Dapper/TypeConverter integrations are intentionally left as follow-ups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on-blocking

The single "Build and analyze" step wrapped build+test inside SonarCloud
begin...end, so an expired/rejected SONAR_TOKEN (auth failure in `begin`)
failed the whole job even though the build succeeded and all tests passed.

- Split Build and Test into their own required steps (the hard gate).
- Gate all SonarCloud steps on `env.SONAR_TOKEN != ''` and mark the analyze
  step `continue-on-error: true`, so credential/infra problems in the advisory
  analysis can never again fail an otherwise-green build (also unblocks
  fork PRs, which don't receive the secret).
- SonarCloud still runs its own build between begin/end and reads the coverage
  reports produced by the Test step, so analysis is unchanged when the token is valid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@SergiySeletsky
SergiySeletsky merged commit 848b2a6 into master Jul 13, 2026
1 check passed
@SergiySeletsky
SergiySeletsky deleted the modernize-unifiedid-interfaces-stj branch July 13, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant