Repository navigation
(PATCH): Move the Roslyn builder behind PatchClassBuilder into RoslynPatchClassBuilder - #134
Merged
PaulTrampert merged 1 commit intoOct 3, 2026
Conversation
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>
✅ PR Title Formatted CorrectlyThe title of this PR has been updated to match the correct format. Thank you! |
Owner
Author
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>
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.
Stacked on #133, which adds
IPatchClassBuilder. Merge that first. GitHub will then retarget this PR tomain.Why
PatchClassBuilderis 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
internal sealed class RoslynPatchClassBuilder : IPatchClassBuilderholds the CodeDom/Roslyn generation and its static cache, unchanged. It has a private constructor and a staticInstance, likeEmitPatchClassBuilder. Its body is moved verbatim fromPatchClassBuilder.cs. Git shows the move as a new file rather than a rename, becausePatchClassBuilder.csstill exists. Usegit log -Lor blame with-Con the old path for the implementation's history.PatchClassBuilderis now a thin facade. It keeps its type,Instance, the obsolete public constructor, and theGetPatchClassForsignature and docs.GetPatchClassFordelegates toRoslynPatchClassBuilder.Instance. Because the cache is static, instances made with the obsolete constructor still share it.EmitPatchClassBuilderandPatchClassModelnow nameRoslynPatchClassBuilder.PatchClassBuilderTestfixture runs againstRoslynPatchClassBuilder.Instance.PatchClassBuilderreturns the Roslyn builder's types.No public API changes and no behaviour changes, so this is PATCH.
Alternatives rejected
PatchClassBuilderas a subclass or alias: this would add public API for no gain, and it would tie the public type to the implementation that Remove the Roslyn-based patch class builder in favour of Reflection.Emit #126 removes.PatchClassBuilder.Instanceto returnIPatchClassBuilder: this is a binary break, because the property signature changes.RoslynPatchClassBuilderpublic: nothing outside the library needs it, and Remove the Roslyn-based patch class builder in favour of Reflection.Emit #126 plans to remove it.SourceGeneratingPatchClassBuilder: the name would be confused with .NET source generators, which run at compile time, while this builder compiles at runtime.Tests
dotnet build: the warning set is identical tomain's.dotnet test: core 124/124, Swashbuckle 17/17 and OpenApi 11/11 pass. The net8.0 projects were run withDOTNET_ROLL_FORWARD=Majorbecause only the .NET 10 runtime is installed locally.🤖 Generated with Claude Code