Skip to content

(PATCH): Move the Roslyn builder behind PatchClassBuilder into RoslynPatchClassBuilder - #134

Merged
PaulTrampert merged 1 commit into
feature/ipatchclassbuilderfrom
feature/roslyn-patch-class-builder
Oct 3, 2026
Merged

PaulTrampert merged 1 commit into
feature/ipatchclassbuilderfrom
feature/roslyn-patch-class-builder

Conversation

@PaulTrampert

@PaulTrampert PaulTrampert commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #133, which adds IPatchClassBuilder. Merge that first. GitHub will then retarget this PR to main.

Why

PatchClassBuilder is both the library's public entry point and the Roslyn implementation. #95 (opt-in Emit builder) and #126 (remove the Roslyn builder) both need the public entry point to stay put while the implementation behind it changes. Splitting them now keeps those changes small. It also gives the Roslyn code its own home if it is reused later as a build-time source generator.

What changed

  • New internal sealed class RoslynPatchClassBuilder : IPatchClassBuilder holds the CodeDom/Roslyn generation and its static cache, unchanged. It has a private constructor and a static Instance, like EmitPatchClassBuilder. Its body is moved verbatim from PatchClassBuilder.cs. Git shows the move as a new file rather than a rename, because PatchClassBuilder.cs still exists. Use git log -L or blame with -C on the old path for the implementation's history.
  • PatchClassBuilder is now a thin facade. It keeps its type, Instance, the obsolete public constructor, and the GetPatchClassFor signature and docs. GetPatchClassFor delegates to RoslynPatchClassBuilder.Instance. Because the cache is static, instances made with the obsolete constructor still share it.
  • Comments in EmitPatchClassBuilder and PatchClassModel now name RoslynPatchClassBuilder.
  • Tests:
    • The shared PatchClassBuilderTest fixture runs against RoslynPatchClassBuilder.Instance.
    • The Roslyn-specific tests (concurrent first use, the public-only errors) call it directly.
    • The shared-cache test also asserts that PatchClassBuilder returns the Roslyn builder's types.

No public API changes and no behaviour changes, so this is PATCH.

Alternatives rejected

Tests

  • dotnet build: the warning set is identical to main's.
  • dotnet test: core 124/124, Swashbuckle 17/17 and OpenApi 11/11 pass. The net8.0 projects were run with DOTNET_ROLL_FORWARD=Major because only the .NET 10 runtime is installed locally.

🤖 Generated with Claude Code

PatchClassBuilder becomes a thin public facade over a new internal
RoslynPatchClassBuilder, which holds the CodeDom/Roslyn implementation
and its static cache. PatchClassBuilder keeps its Instance, its obsolete
constructor and its GetPatchClassFor signature, so nothing breaks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown

✅ PR Title Formatted Correctly

The title of this PR has been updated to match the correct format. Thank you!

@PaulTrampert
PaulTrampert merged commit 1f7bb62 into feature/ipatchclassbuilder Oct 3, 2026
2 checks passed
@PaulTrampert
PaulTrampert deleted the feature/roslyn-patch-class-builder branch October 3, 2026 23:46
@PaulTrampert

Copy link
Copy Markdown
Owner Author

Shown as merged only because #133's branch was fast-forwarded to include this commit; nothing reached main from here. This change is now part of #133.

PaulTrampert added a commit that referenced this pull request Oct 3, 2026
… an internal RoslynPatchClassBuilder (#133)

## Why

The library has two ways to build a patch class: the Roslyn builder,
which lives inside the public `PatchClassBuilder`, and the internal
`EmitPatchClassBuilder`. They share no abstraction, so callers that
choose between them, such as the shared test fixture, pass method groups
around as `Func<Type, Type>`. `PatchClassBuilder` is also both the
public entry point and the Roslyn implementation. #95 (opt-in Emit
builder) and #126 (remove the Roslyn builder) both need the entry point
to stay put while the implementation behind it changes.

## What changed

- New public interface `IPatchClassBuilder` with `Type
GetPatchClassFor(Type type)`.
- New `internal sealed class RoslynPatchClassBuilder :
IPatchClassBuilder` holds the CodeDom/Roslyn generation and its static
cache, moved verbatim from `PatchClassBuilder`. It has a private
constructor and a static `Instance`.
- `PatchClassBuilder` implements `IPatchClassBuilder` and is now a thin
facade. It keeps its type, `Instance`, the obsolete public constructor,
and the `GetPatchClassFor` signature and docs. `GetPatchClassFor`
delegates to `RoslynPatchClassBuilder.Instance`. Because the cache is
static, instances made with the obsolete constructor still share it.
- `EmitPatchClassBuilder` changes from an `internal static class` to an
`internal sealed class` that implements the interface, with a private
constructor and a static `Instance`. Its cache, `AssemblyName` and the
generation helpers are unchanged.
- Tests:
- The shared `PatchClassBuilderTest` fixture takes an
`IPatchClassBuilder` and runs against `RoslynPatchClassBuilder.Instance`
and `EmitPatchClassBuilder.Instance`.
  - The Roslyn-specific tests call `RoslynPatchClassBuilder` directly.
- The shared-cache test also asserts that `PatchClassBuilder` returns
the Roslyn builder's types.

No behaviour changes. The only public API change is the new interface,
which is additive, so this is MINOR.

## Alternatives rejected

- **Changing `PatchClassBuilder.Instance` to return
`IPatchClassBuilder`:** this is a binary break, because the property
signature changes.
- **Renaming the public class, or making `RoslynPatchClassBuilder`
public:** this would add public API that #126 would later have to
remove.
- **Naming it `SourceGeneratingPatchClassBuilder`:** the name would be
confused with .NET source generators, which run at compile time, while
this builder compiles at runtime.
- **Making `EmitPatchClassBuilder` public now:** that belongs with the
opt-in flag in #95.

## Tests

- `dotnet build`: the warning set is identical to `main`'s.
- `dotnet test`: core 124/124, Swashbuckle 17/17 and OpenApi 11/11 pass.
The net8.0 projects were run with `DOTNET_ROLL_FORWARD=Major` because
only the .NET 10 runtime is installed locally.

Supersedes #134, which was stacked on this PR and is now folded into it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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