Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,6 @@ Thumbs.db
**/*.DotSettings.user
/.claude/do_not_commit/
/nupkg/

# Internal specs — not tracked in source control
docs/specs/
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.2.1</VersionPrefix>
<VersionPrefix>1.3.0</VersionPrefix>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/layeredcraft/optimized-enums</RepositoryUrl>
<RepositoryType>git</RepositoryType>
Expand Down
5 changes: 5 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,10 @@
<PackageVersion Include="Verify.XunitV3" Version="31.15.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3.mtp-v2" Version="3.2.2" />
<!-- EFCore package -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.4" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.4.0" />
</ItemGroup>
</Project>
3 changes: 3 additions & 0 deletions LayeredCraft.OptimizedEnums.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
</Folder>
<Folder Name="/docs/usage/">
<File Path="docs\usage\defining-enums.md" />
<File Path="docs\usage\ef-core.md" />
<File Path="docs\usage\json-serialization.md" />
<File Path="docs\usage\lookups.md" />
<File Path="docs\usage\string-values.md" />
Expand All @@ -54,11 +55,13 @@
<Project Path="src/LayeredCraft.OptimizedEnums/LayeredCraft.OptimizedEnums.csproj" />
<Project Path="src/LayeredCraft.OptimizedEnums.Generator/LayeredCraft.OptimizedEnums.Generator.csproj" />
<Project Path="src/LayeredCraft.OptimizedEnums.SystemTextJson.Generator/LayeredCraft.OptimizedEnums.SystemTextJson.Generator.csproj" />
<Project Path="src/LayeredCraft.OptimizedEnums.EFCore.Generator/LayeredCraft.OptimizedEnums.EFCore.Generator.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/LayeredCraft.OptimizedEnums.Tests/LayeredCraft.OptimizedEnums.Tests.csproj" />
<Project Path="tests/LayeredCraft.OptimizedEnums.Generator.Tests/LayeredCraft.OptimizedEnums.Generator.Tests.csproj" />
<Project Path="tests/LayeredCraft.OptimizedEnums.SystemTextJson.Tests/LayeredCraft.OptimizedEnums.SystemTextJson.Tests.csproj" />
<Project Path="tests/LayeredCraft.OptimizedEnums.EFCore.Tests/LayeredCraft.OptimizedEnums.EFCore.Tests.csproj" />
<Project Path="tests/LayeredCraft.OptimizedEnums.Benchmarks/LayeredCraft.OptimizedEnums.Benchmarks.csproj" />
</Folder>
</Solution>
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
|---------|-------|-----------|
| **LayeredCraft.OptimizedEnums** | [![NuGet](https://img.shields.io/nuget/v/LayeredCraft.OptimizedEnums.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums) | [![Downloads](https://img.shields.io/nuget/dt/LayeredCraft.OptimizedEnums.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums/) |
| **LayeredCraft.OptimizedEnums.SystemTextJson** | [![NuGet](https://img.shields.io/nuget/v/LayeredCraft.OptimizedEnums.SystemTextJson.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums.SystemTextJson) | [![Downloads](https://img.shields.io/nuget/dt/LayeredCraft.OptimizedEnums.SystemTextJson.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums.SystemTextJson/) |
| **LayeredCraft.OptimizedEnums.EFCore** | _coming soon_ | |
| **LayeredCraft.OptimizedEnums.EFCore** | [![NuGet](https://img.shields.io/nuget/v/LayeredCraft.OptimizedEnums.EFCore.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums.EFCore) | [![Downloads](https://img.shields.io/nuget/dt/LayeredCraft.OptimizedEnums.EFCore.svg)](https://www.nuget.org/packages/LayeredCraft.OptimizedEnums.EFCore/) |
| **LayeredCraft.OptimizedEnums.Dapper** | _coming soon_ | |
| **LayeredCraft.OptimizedEnums.AutoFixture** | _coming soon_ | |

Expand Down Expand Up @@ -119,6 +119,42 @@ public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int>

Two strategies are available: `ByName` (serializes as the member name string) and `ByValue` (serializes as the underlying value). See the [JSON Serialization docs](https://layeredcraft.github.io/optimized-enums/usage/json-serialization/) for full details.

## Entity Framework Core

Add `LayeredCraft.OptimizedEnums.EFCore` for source-generated, zero-reflection EF Core value converter support. One package is all you need — it pulls in the core package automatically:

```bash
dotnet add package LayeredCraft.OptimizedEnums.EFCore
```

Decorate your class with `[OptimizedEnumEfCore]` and the generator emits concrete `ValueConverter` classes and registration helpers:

```csharp
using LayeredCraft.OptimizedEnums;
using LayeredCraft.OptimizedEnums.EFCore;

[OptimizedEnumEfCore(OptimizedEnumEfCoreStorage.ByValue)]
public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int>
{
public static readonly OrderStatus Pending = new(1, nameof(Pending));
public static readonly OrderStatus Paid = new(2, nameof(Paid));
public static readonly OrderStatus Shipped = new(3, nameof(Shipped));

private OrderStatus(int value, string name) : base(value, name) { }
}
```

Register conversions with the global convention hook in your `DbContext`:

```csharp
protected override void ConfigureConventions(ModelConfigurationBuilder builder)
{
builder.ConfigureOptimizedEnums();
}
```

Two strategies are available: `ByValue` (stores the underlying value) and `ByName` (stores the member name string). See the [Entity Framework Core docs](https://layeredcraft.github.io/optimized-enums/usage/ef-core/) for full details.

## Installation

```bash
Expand Down
80 changes: 80 additions & 0 deletions docs/advanced/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,86 @@ public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int> { ...
public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int> { ... }
```

## EFCore Diagnostics

The `LayeredCraft.OptimizedEnums.EFCore` generator emits diagnostics with the `OE3xxx` prefix.

### OE3001 — Not an OptimizedEnum

**Message:** `The class '{0}' must inherit from OptimizedEnum<TEnum, TValue> to use [OptimizedEnumEfCore]`

**Cause:** `[OptimizedEnumEfCore]` was applied to a class that does not inherit from `OptimizedEnum<TEnum, TValue>`.

**Fix:** Remove the attribute, or make the class inherit from `OptimizedEnum<TEnum, TValue>` (directly or through an abstract intermediate base class).

### OE3002 — Must Be Partial

**Message:** `The class '{0}' must be declared as partial for [OptimizedEnumEfCore] source generation`

**Cause:** A class decorated with `[OptimizedEnumEfCore]` is missing the `partial` keyword.

**Fix:**
```csharp
// Before
[OptimizedEnumEfCore]
public sealed class OrderStatus : OptimizedEnum<OrderStatus, int> { ... }

// After
[OptimizedEnumEfCore]
public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int> { ... }
```

### OE3003 — Unknown Storage Type

**Message:** `The class '{0}' specifies an unknown OptimizedEnumEfCoreStorage value '{1}'; valid values are ByValue (0) and ByName (1)`

**Cause:** An explicit integer cast was used to pass an undefined `OptimizedEnumEfCoreStorage` value to `[OptimizedEnumEfCore]`.

**Fix:** Use only the defined enum members:
```csharp
[OptimizedEnumEfCore(OptimizedEnumEfCoreStorage.ByValue)] // or ByName
public sealed partial class OrderStatus : OptimizedEnum<OrderStatus, int> { ... }
```

### OE3004 — Unsupported Target

**Cause:** `[OptimizedEnumEfCore]` was applied to a class configuration that the generator does not support. Two confirmed triggers:

**Abstract class:**

```csharp
// Wrong — abstract base class
[OptimizedEnumEfCore]
public abstract partial class OrderStatusBase : OptimizedEnum<OrderStatusBase, int> { }

// Correct — concrete derived class
[OptimizedEnumEfCore]
public sealed partial class OrderStatus : OrderStatusBase<OrderStatus> { ... }
```

**Enum nested inside a generic containing type:**

```csharp
// Wrong — containing type has type parameters
public class Container<T>
{
[OptimizedEnumEfCore]
public sealed partial class Status : OptimizedEnum<Status, int> { ... }
}
```

EF Core converters and extension methods are emitted at namespace scope. Generic type parameters from the containing type would not be in scope there, producing uncompilable references. Move the enum out of the generic container, or remove `[OptimizedEnumEfCore]` and register the conversion manually.

Enums nested inside **non-generic** containing types are fully supported.

### OE9003 — Internal Generator Error

**Message:** `An unexpected error occurred while generating the EF Core support for '{0}': {1}`

**Cause:** An unhandled exception occurred inside the EFCore generator. This is a generator bug.

**Fix:** Report the issue with the full diagnostic message and the enum source code. As a workaround, the EFCore attribute can be temporarily removed from the affected type.

## Generator Not Running?

If you add the package but see no generated members, check:
Expand Down
24 changes: 24 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,30 @@ To add source-generated `System.Text.Json` converter support, install the System

See [JSON Serialization](../usage/json-serialization.md) for usage details.

## Optional: Entity Framework Core

To add source-generated EF Core value converter support, install the EFCore package. It declares the core package as a dependency:

=== ".NET CLI"

```bash
dotnet add package LayeredCraft.OptimizedEnums.EFCore
```

=== "Package Manager"

```powershell
Install-Package LayeredCraft.OptimizedEnums.EFCore
```

=== "PackageReference"

```xml
<PackageReference Include="LayeredCraft.OptimizedEnums.EFCore" Version="x.x.x" />
```

Supports EF Core 8, 9, and 10. See [Entity Framework Core](../usage/ef-core.md) for usage details.

## Verifying the Installation

After adding the package, define a type that inherits from `OptimizedEnum<TEnum, TValue>` and declare it `partial`. Build the project — the generator runs during compilation and produces the lookup members. You can inspect the generated output under `obj/` or via your IDE's "Go to definition" on any generated method.
Expand Down
Loading
Loading