Skip to content

feat: add LayeredCraft.OptimizedEnums.EFCore package#10

Merged
ncipollina merged 7 commits intomainfrom
feat/efcore-package
Apr 12, 2026
Merged

feat: add LayeredCraft.OptimizedEnums.EFCore package#10
ncipollina merged 7 commits intomainfrom
feat/efcore-package

Conversation

@ncipollina
Copy link
Copy Markdown
Contributor

Summary

  • Adds LayeredCraft.OptimizedEnums.EFCore — a new source generator package that emits zero-reflection, AOT-safe EF Core value converters for OptimizedEnum types
  • Decorate a sealed partial OptimizedEnum class with [OptimizedEnumEfCore(ByValue|ByName)] to get {Prefix}ValueConverter, {Prefix}NameConverter, enum-specific HasXxxConversionByValue/ByName() extension methods, and a shared ConfigureOptimizedEnums() convention hook — all generated at compile time
  • Diagnostics: OE3001 (not OptimizedEnum), OE3002 (not partial), OE3003 (unknown storage type), OE3004 (abstract class), OE9003 (internal error)

Test plan

  • 12 generator snapshot tests (ByValue/ByName × namespace/global/string-value/nested/abstract-base + 4 error cases), verified across net8/9/10
  • 8 InMemory integration tests (round-trip, global convention, property override, nullable null/non-null, intermediate abstract base, explicit converter helpers)
  • 4 PostgreSQL integration tests via Testcontainers (PK as enum, alternate key, index, schema creation)
  • Full solution: 234/234 tests pass across all TFMs

🤖 Generated with Claude Code

Adds a new source generator package that emits EF Core value converters
and property builder extension methods for OptimizedEnum types. Decorate
a sealed partial OptimizedEnum class with [OptimizedEnumEfCore] to get
ByValue and ByName converters, plus a ConfigureOptimizedEnums() convention
hook, generated at compile time with zero reflection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a 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.

Reviewed commit: 2f81960c81

ℹ️ 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".

- Remove unused EfCoreSyntaxProvider_FilterErrors constant from TrackingNames
- Fix O(n²) Insert(0,...) pattern in GetContainingTypeSimpleNames with Add+Reverse
- Thread CancellationToken through all async EF Core calls in integration tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new LayeredCraft.OptimizedEnums.EFCore source generator package plus a dedicated test suite and technical spec to support zero-reflection, AOT-safe EF Core value conversions for OptimizedEnum types.

Changes:

  • Introduces LayeredCraft.OptimizedEnums.EFCore.Generator incremental generator with attribute injection + per-enum converter/extension emission + shared conventions hook.
  • Adds EF Core generator snapshot tests (Verify) and EF integration tests (InMemory + PostgreSQL via Testcontainers).
  • Adds EF Core package technical specification and central package versions for EF Core/Npgsql/Testcontainers.

Reviewed changes

Copilot reviewed 73 out of 73 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/xunit.runner.json xUnit runner configuration for the new EFCore test project.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/ModuleInitializer.cs Initializes Verify.SourceGenerators for the EFCore test suite.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/LayeredCraft.OptimizedEnums.EFCore.Tests.csproj New multi-targeted EFCore test project setup + dependencies.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/IntegrationTests/TestEnums.cs Test OptimizedEnum types used by EF integration tests.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/IntegrationTests/TestDbContext.cs InMemory + relational DbContexts and manual converters for integration tests.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/IntegrationTests/RelationalTests.cs PostgreSQL/Testcontainers integration coverage for keys/indexes.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/IntegrationTests/ConversionTests.cs InMemory integration coverage for conversions and null behavior.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/GeneratorTests/GeneratorVerifyTests.cs Snapshot test cases for generator outputs + diagnostics.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/GeneratorTests/GeneratorTestHelpers.cs Roslyn harness to run generators and Verify outputs.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_WithNamespace#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByValue, namespaced).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_WithNamespace#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByValue, namespaced).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_WithNamespace#MyApp.Domain.OrderStatus.g.verified.cs Verify snapshot: core OptimizedEnum generation for OrderStatus.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_WithNamespace#MyApp.Domain.OrderStatus.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for OrderStatus.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_WithNamespace#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByName, namespaced).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_WithNamespace#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByName, namespaced).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_WithNamespace#MyApp.Domain.OrderStatus.g.verified.cs Verify snapshot: core OptimizedEnum generation for OrderStatus (ByName case).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_WithNamespace#MyApp.Domain.OrderStatus.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for OrderStatus (ByName default).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_GlobalNamespace#Priority.g.verified.cs Verify snapshot: core OptimizedEnum generation for global-ns Priority.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_GlobalNamespace#Priority.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for global-ns Priority (ByValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_GlobalNamespace#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByValue, global-ns).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_GlobalNamespace#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByValue, global-ns).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_GlobalNamespace#Priority.g.verified.cs Verify snapshot: core OptimizedEnum generation for Priority (ByName case).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_GlobalNamespace#Priority.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for Priority (ByName).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_GlobalNamespace#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByName, global-ns).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_GlobalNamespace#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByName, global-ns).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_StringValueType#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByValue, string TValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_StringValueType#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByValue, string TValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_StringValueType#MyApp.Domain.Color.g.verified.cs Verify snapshot: core OptimizedEnum generation for Color (string TValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByValue_StringValueType#MyApp.Domain.Color.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for Color (ByValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_StringValueType#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output (ByName, string TValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_StringValueType#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source (ByName, string TValue).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_StringValueType#MyApp.Domain.Color.g.verified.cs Verify snapshot: core OptimizedEnum generation for Color (ByName case).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.ByName_StringValueType#MyApp.Domain.Color.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for Color (ByName).
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.NestedType#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for nested OptimizedEnum type.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.NestedType#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source for nested-type scenario.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.NestedType#MyApp.Domain.Outer.Status.g.verified.cs Verify snapshot: core OptimizedEnum generation for nested Outer.Status.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.NestedType#MyApp.Domain.Outer.Status.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions for nested Outer.Status.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.IntermediateAbstractBase#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for intermediate-base scenario.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.IntermediateAbstractBase#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: injected attribute source for intermediate-base scenario.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.IntermediateAbstractBase#MyApp.Domain.OrderStatus.g.verified.cs Verify snapshot: core OptimizedEnum generation via abstract base.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.IntermediateAbstractBase#MyApp.Domain.OrderStatus.EFCore.g.verified.cs Verify snapshot: EFCore converters/extensions via abstract base.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotOptimizedEnum#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for OE3001 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotOptimizedEnum#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: attribute source for OE3001 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotOptimizedEnum.verified.txt Verify snapshot: OE3001 diagnostic output.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotPartial#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for OE3002 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotPartial#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: attribute source for OE3002 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_NotPartial.verified.txt Verify snapshot: OE3002 (and OE0001) diagnostic output.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_UnknownStorageType#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for OE3003 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_UnknownStorageType#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: attribute source for OE3003 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_UnknownStorageType#MyApp.Domain.OrderStatus.g.verified.cs Verify snapshot: core OptimizedEnum output during OE3003 case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_UnknownStorageType.verified.txt Verify snapshot: OE3003 diagnostic output.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_AbstractClass#OptimizedEnumEfCoreConventions.g.verified.cs Verify snapshot: conventions output for OE3004 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_AbstractClass#OptimizedEnumEfCoreAttribute.g.verified.cs Verify snapshot: attribute source for OE3004 failure case.
tests/LayeredCraft.OptimizedEnums.EFCore.Tests/Snapshots/GeneratorVerifyTests.Error_AbstractClass.verified.txt Verify snapshot: OE3004 diagnostic output.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/LayeredCraft.OptimizedEnums.EFCore.Generator.csproj New generator package project + packing into analyzers.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/OptimizedEnumEfCoreGenerator.cs Incremental generator wiring: attribute injection + outputs.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/TrackingNames.cs Incremental generator tracking names for diagnostics/perf tooling.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/AttributeSource.cs Post-init injected attribute + storage enum source text.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Providers/EfCoreSyntaxProvider.cs Attribute discovery + model building + diagnostics (OE3001-4).
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Models/EfCoreInfo.cs Immutable model for EFCore generation inputs.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Models/EquatableArray.cs Utility for stable incremental equality.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Models/LocationInfo.cs Location capture/translation utilities for diagnostics.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Diagnostics/DiagnosticInfo.cs Diagnostic wrapper + reporting helpers.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Diagnostics/DiagnosticDescriptors.cs EFCore generator diagnostics definitions (OE3001-4, OE9003).
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Emitters/EfCoreEmitter.cs Renders per-enum and conventions outputs via Scriban.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Emitters/TemplateHelper.cs Embedded Scriban template loading + caching.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Templates/OptimizedEnumEfCore.scriban Template for per-enum converters + extension methods.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/Templates/OptimizedEnumEfCoreConventions.scriban Template for shared ConfigureOptimizedEnums() convention hook.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/AnalyzerReleases.Unshipped.md Roslyn analyzer release tracking for new diagnostics.
src/LayeredCraft.OptimizedEnums.EFCore.Generator/AnalyzerReleases.Shipped.md Placeholder shipped analyzer tracking file.
LayeredCraft.OptimizedEnums.slnx Adds EFCore generator + EFCore tests projects to solution.
docs/specs/efcore-package-spec.md Full technical specification for the EFCore package.
Directory.Packages.props Adds central versions for EF Core + Npgsql + Testcontainers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ncipollina and others added 3 commits April 11, 2026 14:16
- Change extension class from `public` to `internal` to avoid
  inconsistent-accessibility errors when the annotated enum is internal
- Add `_` separator in BuildConverterPrefix to prevent collisions between
  nested-type paths that would otherwise concatenate ambiguously
  (e.g. A.BC.Status and AB.C.Status both produced ABCStatus before)
- Update spec: document why nullable PropertyBuilder<T?> overloads are
  omitted (CS0111 — reference-type generics make T and T? the same
  instantiation) and why no ValueComparer is generated (EF Core's default
  comparer is sufficient for immutable singletons; standalone HasComparer
  API does not exist)
- Update verified snapshots to match new output

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add docs/usage/ef-core.md covering installation, storage strategies,
  all three registration approaches, nullable properties, PK/FK/index
  usage, string-valued enums, abstract bases, nested types, AOT safety,
  generated code examples, and v1 limitations
- Add OE3001–OE3004 and OE9003 EFCore diagnostics section to
  docs/advanced/diagnostics.md
- Add EFCore install instructions to docs/getting-started/installation.md
- Update README: add EFCore section with quick-start example; replace
  "coming soon" with NuGet/download badges
- Add "Entity Framework Core" to zensical.toml nav under Usage
- Add docs/usage/ef-core.md and docs/specs/ folder to .slnx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Delete the specs folder and its contents, add docs/specs/ to .gitignore,
and remove the /docs/specs/ folder entry from the solution file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ncipollina
Copy link
Copy Markdown
Contributor Author

@codex review

@ncipollina
Copy link
Copy Markdown
Contributor Author

@claude review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a 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.

Reviewed commit: c4dc8a9f3f

ℹ️ 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".

Copy link
Copy Markdown

@j-d-ha j-d-ha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks Great! One small request around docs.

ncipollina and others added 2 commits April 11, 2026 20:01
…o 1.3.0

Extension methods must be in top-level static classes (CS1109), and converters
emitted at namespace scope cannot reference free type parameters from a generic
containing type. Emit OE3004 with a clear message when the annotated enum's
containing type chain includes a generic type.

Also captures ContainingTypeDeclarations in the model (full partial-class
declarations for each containing type) for future use if the preamble/suffix
pattern is needed for other generated members.

Bump VersionPrefix from 1.2.1 to 1.3.0 for the new EFCore package release.

Update diagnostics.md and ef-core.md to document the generic container
limitation under OE3004.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove Package Manager (PowerShell) tab from installation section
- Expand ConfigureConventions snippet to show full DbContext context,
  making it clear where the method override belongs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ncipollina ncipollina requested a review from j-d-ha April 12, 2026 00:26
Copy link
Copy Markdown

@j-d-ha j-d-ha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Looks great! Love this and will be using it.

@ncipollina ncipollina merged commit 3245db2 into main Apr 12, 2026
3 checks passed
@ncipollina ncipollina deleted the feat/efcore-package branch April 12, 2026 00:35
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.

3 participants