-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Add instance registration support for singleton decorators (Phase 3 of #3) #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…se 3 of #3) This completes issue #3 by adding support for singleton instance registrations. **New Features:** - Support for `AddSingleton<TService>(instance)` registrations - Decorators are applied around pre-created instances - Instance type extraction from argument expressions - Factory lambda wrapping for keyed service compatibility **Implementation Details:** - Added `InstanceSingleTypeParam` to `RegistrationKind` enum - Added `InstanceParameterName` field to model - Updated provider to detect non-delegate instance parameters - Extended emitter to generate instance interceptors - Instance wrapped in factory: `(sp, _) => capturedInstance` - Type extraction via `SemanticModel.GetTypeInfo(instanceArg).Type` **Limitations:** - Only `AddSingleton` supported (Scoped/Transient don't have instance overloads) - Instance must be created before registration **Testing:** - 3 new test cases (047-049) with snapshot verification - Updated sample project with instance registration example - All tests passing **Documentation:** - Created docs/advanced/instance-registrations.md - Updated CLAUDE.md with supported patterns - Updated README.md with feature list - Updated changelog and release notes - Version bumped to 1.0.4-beta 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ChatGPT identified that wrapping instances in factory lambdas broke .NET DI disposal semantics. The container would dispose user-provided instances when it shouldn't. **Root Cause:** Line 421 wrapped instances in factory lambda: `(sp, _) => capturedInstance` This changed disposal ownership - factory-created services are disposed by the container, but instance registrations should NOT be. **Fix:** Use direct instance overload: `AddKeyedSingleton<T>(key, instance)` This preserves expected .NET DI disposal behavior. **Changes:** - InterceptorEmitter.cs: Removed factory lambda wrapper (line 421) - docs/advanced/instance-registrations.md: Updated to reflect direct registration - CLAUDE.md: Updated generated code example - Test snapshots regenerated with correct code **Verification:** - Build succeeds - Sample project runs correctly - Disposal semantics now match .NET DI expectations Fixes disposal bug reported by ChatGPT in PR #13 feedback. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Added Example 8 demonstrating keyed instance registration pattern. Note: This currently does NOT apply decorators because we don't yet intercept the AddKeyedSingleton<T>(key, instance) signature. This is a known limitation and potential future enhancement. The example shows correct usage and expected resolution pattern using GetRequiredKeyedService<T>(key). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
Extends instance registration support to handle keyed singleton instances.
Users can now register pre-created instances with keys and have decorators
applied automatically.
Key changes:
- Add KeyedInstanceSingleTypeParam to RegistrationKind enum
- Update ClosedGenericRegistrationProvider to detect keyed instance pattern
- Validates AddKeyedSingleton<T>(key, instance) signature
- Extracts implementation type from instance argument (2nd parameter)
- Add EmitKeyedInstanceSingleTypeParamInterceptor to InterceptorEmitter
- Uses nested key strategy to avoid circular resolution
- Registers instance with nested key, decorated factory with user key
- Add test case 051_KeyedInstanceRegistration_SingleDecorator
- Update instance registrations documentation with keyed examples
Pattern supported:
services.AddKeyedSingleton<IRepository<Customer>>("primary", instance);
Generated code preserves disposal semantics by using direct instance
registration instead of factory lambda wrapper.
Fixes sample project Example 8 - now applies decorators to keyed instances.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements instance registration support for singleton decorators, completing Phase 3 of issue #3. DecoWeaver can now intercept and decorate pre-created instances registered with AddSingleton<TService>(instance) and AddKeyedSingleton<TService>(key, instance).
Key Changes:
- Added instance registration detection by distinguishing non-delegate parameters from factory delegates
- Implemented type extraction from instance arguments using Roslyn's semantic model
- Generated interceptors that wrap instances in factory lambdas for keyed service registration
Reviewed Changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
ClosedGenericRegistrationProvider.cs |
Added instance parameter detection, type extraction logic, and new registration kinds |
InterceptorEmitter.cs |
Added methods to emit instance interceptors with factory lambda wrapping |
DecoWeaverGeneratorTests.cs |
Added four new test cases covering instance registration scenarios |
| Test case files (047-051) | New test input files for instance registration scenarios |
| Generated snapshot files | Verified output for instance registration interceptors |
| Documentation files | Added comprehensive guide and updated changelog/release notes |
| Sample program | Added instance registration examples |
| Version files | Bumped version to 1.0.4-beta |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void Save(T entity); | ||
| } | ||
|
|
||
| [DecoratedBy(typeof(LoggingRepository<>))] |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The DecoratedBy attribute uses typeof() syntax instead of the generic type parameter syntax shown in other examples. For consistency with the codebase style and C# 11+ syntax, consider using [DecoratedBy<LoggingRepository<>>].
| [DecoratedBy(typeof(LoggingRepository<>))] | |
| [DecoratedBy<LoggingRepository<>>] |
| ### Added | ||
| - No changes yet | ||
|
|
||
| ## [1.0.4-beta] - 2025-11-13 |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The date shows November 13, 2025, but the current date is November 2025. Verify this is the intended release date or update to the actual date when this version was released/will be released.
| ## [1.0.4-beta] - 2025-11-13 | |
| ## [1.0.4-beta] - [Unreleased] |
| - `AddSingleton<TService>(instance)` - Single type parameter with instance | ||
| - Decorators are applied around the provided instance | ||
| - Only `AddSingleton` is supported (instance registrations don't exist for Scoped/Transient in .NET DI) | ||
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The release notes mention 'Single type parameter with instance' but don't mention keyed instance registration support (AddKeyedSingleton), which is also implemented in this PR and shown in the test cases. Consider adding a bullet point for keyed instance registration.
| * **Keyed instance registration support** - Decorators now work with keyed singleton instance registrations: | |
| - `AddKeyedSingleton<TService>(key, instance)` - Register a singleton instance with a key | |
| - Decorators are applied around the provided keyed instance |
| var svcTypeParam = symbolToCheck.TypeParameters.Length >= 1 ? symbolToCheck.TypeParameters[0] : null; | ||
| if (svcTypeParam == null || !SymbolEqualityComparer.Default.Equals(param2.Type, svcTypeParam)) | ||
| return null; |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The check symbolToCheck.TypeParameters.Length >= 1 could be simplified to symbolToCheck.TypeParameters.Length > 0 for clarity, or better yet, use symbolToCheck.TypeParameters.FirstOrDefault() to avoid the ternary operator.
| var svcTypeParam = symbolToCheck.TypeParameters.Length >= 1 ? symbolToCheck.TypeParameters[0] : null; | ||
| if (svcTypeParam == null || !SymbolEqualityComparer.Default.Equals(param3.Type, svcTypeParam)) | ||
| return null; |
Copilot
AI
Nov 14, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is duplicated from lines 154-156. Consider extracting this validation into a helper method like ValidateInstanceParameter(IParameterSymbol param, IMethodSymbol symbol) to reduce duplication.
|
Codex Review: Didn't find any major issues. Can't wait for the next one! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
This PR completes issue #3 by implementing Phase 3: Instance Registration Support. DecoWeaver now supports decorating singleton instances registered with
AddSingleton<TService>(instance).Closes #3
Features
AddSingleton<TService>(instance)AddSingletonsupported (Scoped/Transient don't have instance parameters in .NET DI)Implementation
Core Changes
InstanceSingleTypeParamtoRegistrationKindenumInstanceParameterNamefield toClosedGenericRegistrationmodelClosedGenericRegistrationProviderto detect instance registrations (non-delegate parameters)InterceptorEmitterto generate instance interceptors with factory lambda wrappingSemanticModel.GetTypeInfo(instanceArg).TypeKey Technical Details
Func<>delegatenew SqlRepository<Customer>())services.AddKeyedSingleton<T>(key, (sp, _) => capturedInstance)thisparameter, so instance is atargs[0]Testing
Documentation
docs/advanced/instance-registrations.mdwith comprehensive guideCLAUDE.mdwith supported patterns sectionREADME.mdfeature listdocs/changelog.mdfor v1.0.4-betareleasenotes.propswith release notesExamples
Basic Usage
Generated Code
Test Plan
Breaking Changes
None - this is purely additive functionality.
Related Issues
🤖 Generated with Claude Code