feat: add Identity Map pattern#252
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Test Results 1 files 1 suites 2m 37s ⏱️ Results for commit 3560892. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Pull request overview
Adds first-class Identity Map support across PatternKit’s runtime, source generators, examples, tests, and documentation so applications can reuse object instances per key within a scope and detect duplicate-instance conflicts.
Changes:
- Introduces
IIdentityMap<TEntity, TKey>/IdentityMap<TEntity, TKey>runtime implementation and results/status types. - Adds
[GenerateIdentityMap]/[IdentityMapKeySelector]attributes plus an incremental generator with diagnostics and tests. - Adds an Order Identity Map example (DI-scoped), updates pattern/example catalogs, and documents the new pattern + generator.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/PatternKit.Tests/Application/IdentityMap/IdentityMapTests.cs | Adds runtime Identity Map behavior tests (reuse, conflict, remove/clear). |
| test/PatternKit.Generators.Tests/IdentityMapGeneratorTests.cs | Adds generator tests for emitted factory + diagnostics. |
| test/PatternKit.Generators.Tests/AbstractionsAttributeCoverageTests.cs | Extends attribute coverage tests for Identity Map attributes. |
| test/PatternKit.Examples.Tests/ProductionReadiness/PatternKitPatternCatalogTests.cs | Updates pattern catalog assertions to include Identity Map. |
| test/PatternKit.Examples.Tests/IdentityMapDemo/OrderIdentityMapDemoTests.cs | Adds example tests for fluent/generated paths and DI scope behavior. |
| test/PatternKit.Examples.Tests/DependencyInjection/PatternKitExampleDependencyInjectionTests.cs | Verifies Identity Map example is registered and runnable via IoC. |
| src/PatternKit.Generators/IdentityMap/IdentityMapGenerator.cs | Implements the Identity Map incremental source generator and diagnostics. |
| src/PatternKit.Generators/AnalyzerReleases.Unshipped.md | Registers new generator diagnostic IDs (PKIM001-003). |
| src/PatternKit.Generators.Abstractions/IdentityMap/IdentityMapAttributes.cs | Adds generator abstraction attributes for Identity Map. |
| src/PatternKit.Examples/ProductionReadiness/PatternKitPatternCatalog.cs | Adds Identity Map pattern entry to the pattern catalog. |
| src/PatternKit.Examples/ProductionReadiness/PatternKitExampleCatalog.cs | Adds “Order Identity Map Pattern” entry to the example catalog. |
| src/PatternKit.Examples/IdentityMapDemo/OrderIdentityMapDemo.cs | Adds Identity Map demo (fluent + generated), workflow, and DI extensions. |
| src/PatternKit.Examples/DependencyInjection/PatternKitExampleServiceCollectionExtensions.cs | Registers the Identity Map example into the overall examples DI bundle. |
| src/PatternKit.Core/Application/IdentityMap/IdentityMap.cs | Adds runtime Identity Map interfaces/types and in-memory implementation. |
| docs/patterns/toc.yml | Adds Identity Map to the patterns documentation TOC. |
| docs/patterns/application/identity-map.md | Documents the Identity Map pattern and DI usage. |
| docs/guides/pattern-coverage.md | Updates coverage table to include Identity Map. |
| docs/generators/toc.yml | Adds Identity Map generator to generators TOC. |
| docs/generators/index.md | Adds Identity Map generator to generators index table. |
| docs/generators/identity-map.md | Documents generator usage and diagnostics. |
| docs/examples/toc.yml | Adds Order Identity Map example doc to examples TOC. |
| docs/examples/order-identity-map-pattern.md | Documents the Order Identity Map example and DI import usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public IdentityMapResult<TEntity> Track(TEntity entity) | ||
| { | ||
| if (_keySelector is null) | ||
| throw new InvalidOperationException("A key selector is required to track entities without an explicit key."); |
| if (_entities.TryGetValue(key, out var existing)) | ||
| return existing; | ||
|
|
||
| var created = factory(key); |
| private static string GenerateSource(INamedTypeSymbol type, INamedTypeSymbol entityType, INamedTypeSymbol keyType, string selectorName, string factoryName) | ||
| { | ||
| var ns = type.ContainingNamespace.IsGlobalNamespace ? null : type.ContainingNamespace.ToDisplayString(); | ||
| var entityName = entityType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); | ||
| var keyName = keyType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); | ||
| var sb = new StringBuilder(); | ||
| sb.AppendLine("// <auto-generated/>"); | ||
| sb.AppendLine("#nullable enable"); | ||
| sb.AppendLine(); | ||
| if (ns is not null) | ||
| { | ||
| sb.Append("namespace ").Append(ns).AppendLine(";"); | ||
| sb.AppendLine(); | ||
| } | ||
|
|
||
| sb.Append(GetAccessibility(type.DeclaredAccessibility)).Append(' '); | ||
| if (type.IsStatic) | ||
| sb.Append("static "); | ||
| else if (type.IsAbstract && type.TypeKind == TypeKind.Class) | ||
| sb.Append("abstract "); | ||
| else if (type.IsSealed && type.TypeKind == TypeKind.Class) | ||
| sb.Append("sealed "); | ||
| sb.Append("partial ").Append(type.TypeKind == TypeKind.Struct ? "struct" : "class").Append(' ').Append(type.Name).AppendLine(); | ||
| sb.AppendLine("{"); | ||
| sb.Append(" public static global::PatternKit.Application.IdentityMap.IdentityMap<") | ||
| .Append(entityName).Append(", ").Append(keyName).Append("> ").Append(factoryName).AppendLine("()"); | ||
| sb.Append(" => global::PatternKit.Application.IdentityMap.IdentityMap<") | ||
| .Append(entityName).Append(", ").Append(keyName).Append(">.Create(").Append(selectorName).AppendLine(").Build();"); | ||
| sb.AppendLine("}"); |
🔍 PR Validation ResultsVersion: `` ✅ Validation Steps
📊 ArtifactsDry-run artifacts have been uploaded and will be available for 7 days. This comment was automatically generated by the PR validation workflow. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #252 +/- ##
==========================================
+ Coverage 90.77% 96.16% +5.38%
==========================================
Files 324 328 +4
Lines 29850 30049 +199
Branches 4163 4197 +34
==========================================
+ Hits 27097 28896 +1799
+ Misses 1227 1153 -74
+ Partials 1526 0 -1526
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
8555cb0 to
3560892
Compare
Code Coverage |
Summary
Validation
Local examples build is blocked by the existing Roslyn analyzer/compiler mismatch (CS9057), so hosted CI validates the examples path.