Add support for keyed DI services (Issue #165)#186
Conversation
Validators registered via AddKeyedScoped/Transient/Singleton are now discovered automatically by scanning IServiceCollection descriptors and resolving via IKeyedServiceProvider at runtime. This eliminates registration ordering issues and gracefully falls back when keyed services are not used. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@claude review this PR |
|
Claude finished @avgalex's task in 2m 16s —— View job Code Review for PR #186Progress
SummaryThis PR adds support for keyed DI services to discover validators registered via Critical Issues🔴 Compilation Error: Missing
|
…idatorForType behavior The test expected HaveCountGreaterThanOrEqualTo(1) which was too weak. Changed to HaveCount(1) since IsOneValidatorForType defaults to true, meaning only the first validator is returned per type. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Regarding the code review findings:
|
@claude review this PR |
|
Claude finished @avgalex's task in 2m 4s —— View job Code Review for PR #186Progress
SummaryThis PR successfully implements support for keyed DI services (Issue #165). After reviewing the code and addressing the previous feedback, I can confirm: ✅
Detailed Review✅ Core Implementation QualityThe implementation in
✅ Service Registration ApproachThe pattern of registering services.TryAddSingleton<IServiceCollection>(services);This enables keyed validator discovery at resolution time, making the solution registration-order independent. This is a significant usability improvement.
|
|
The test assertion fix (commit 1e53610) has been pushed to the branch. The bot's previous review ran before the push reached All issues from the review are now resolved:
|
@claude review this PR |
|
Claude finished @avgalex's task in 2m 16s —— View job Code Review for PR #186Progress
Summary✅ All Previous Issues Resolved You are absolutely correct — both issues identified in the previous reviews have been properly addressed:
Comprehensive Implementation Review✅ Core Architecture — Excellent DesignFile: The implementation demonstrates several strong architectural decisions: 1. Three-Phase Validator Discovery (lines 85-137) // 1. Non-keyed validators (existing behavior)
// 2. Keyed validators (Issue #165)
// 3. Base type validators (existing behavior)This ordering ensures backward compatibility while seamlessly integrating keyed service support. 2. Reference-Based Deduplication (line 83) var seen = new HashSet<IValidator>(ReferenceEqualityComparer.Instance);Using reference equality is the correct choice here. It prevents the same validator instance from being returned multiple times when registered both as keyed and non-keyed services, without relying on potentially expensive or incorrect value-based equality comparisons. 3. Registration Order Independence (lines 84-85 in ServiceCollectionExtensions) // Issue #165: Register IServiceCollection for keyed validator discovery at resolution time
services.TryAddSingleton<IServiceCollection>(services);This clever approach solves a significant usability issue. By registering the 4. Graceful Degradation (lines 150-151) if (serviceCollection is null || serviceProvider is not IKeyedServiceProvider keyedProvider)
yield break;The implementation gracefully handles scenarios where:
5. Dual Path Support (lines 27-28) return serviceProvider.GetService(validatorType) as IValidator
?? serviceProvider.GetKeyedValidators(modelType).FirstOrDefault();Both ✅ Test Coverage — ComprehensiveFile: The test suite covers all critical scenarios:
The test quality is high, with clear naming, good comments, and realistic usage patterns. ✅ Consistent Implementation Across PackagesBoth Swashbuckle: Both use: // Issue #165: Register IServiceCollection for keyed validator discovery at resolution time
services.TryAddSingleton<IServiceCollection>(services);This ensures feature parity across both integration packages. ✅ Documentation and Version ManagementCHANGELOG.md (lines 1-7):
version.props:
Potential Considerations (Non-Blocking)These are not issues, but observations for future consideration: 💡 Performance: Service Collection IterationFile: The foreach (var descriptor in serviceCollection)
{
if (descriptor.IsKeyedService
&& descriptor.ServiceType == validatorType
&& descriptor.ServiceKey is not null
&& keyedProvider.GetKeyedService(validatorType, descriptor.ServiceKey)
is IValidator validator)
{
yield return validator;
}
}Analysis:
Potential optimization (for future if needed):
Verdict: Current implementation is acceptable. Only optimize if profiling shows this as a bottleneck in real-world scenarios. 💡 Additional Test Scenarios (Optional Enhancement)Consider adding tests for:
These aren't critical but would provide even more comprehensive coverage. Code Quality Assessment✅ Excellent code quality across all changes:
Final Verdict✅ APPROVE — Ready to MergeThis PR successfully implements keyed DI services support with:
All previous review concerns have been addressed:
The implementation is production-ready for Excellent work on this feature! The design is thoughtful, the implementation is clean, and the test coverage is comprehensive. 🎉 |
Summary
AddKeyedScoped,AddKeyedTransient,AddKeyedSingletonare now discovered automaticallyAddFluentValidationRulesToSwagger()is called before or after keyed validators are registeredIKeyedServiceProvider7.0.4-beta.2How it works
AddFluentValidationRulesToSwagger()registersIServiceCollectionas a singleton snapshotGetValidators()first resolves non-keyed validators (existing behavior)IServiceCollectionsnapshot for keyedIValidator<T>descriptorsIKeyedServiceProvider.GetKeyedService()Files changed
ValidatorRegistryExtensions.cs— core resolution logic with newGetKeyedValidatorshelperServiceCollectionExtensions.cs(Swashbuckle + NSwag) — registerIServiceCollectionsingletonKeyedServicesTests.cs— 5 tests covering keyed-only, non-keyed regression, mixed, and full integrationTest plan
Closes #165
🤖 Generated with Claude Code