Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/joseftw/jos.enumeration
https://github.com/joseftw/jos.enumeration
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/joseftw/jos.enumeration
- Owner: joseftw
- License: mpl-2.0
- Created: 2022-09-17T14:15:03.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-01-08T07:45:32.000Z (10 months ago)
- Last Synced: 2024-05-15T23:49:10.003Z (6 months ago)
- Language: C#
- Size: 144 KB
- Stars: 20
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
- RSCG_Examples - Jos.Enumeration,
- csharp-source-generators - JOS.Enumeration - Enumeration class powered by source generation (Source Generators / Enums)
README
# JOS.Enumeration
Enumeration implementation with source generation support.## Installation
### JOS.Enumeration
Contains the `IEnumeration interface` and a `System.Text.Json` JsonConverter.
The `JOS.Enumeration.SourceGenerator` package contains the actual source generator.**Don't forget to install that one as well.** 😃
```
dotnet add package JOS.Enumeration
dotnet add package JOS.Enumeration.SourceGenerator
```### JOS.Enumeration.Database.Dapper
Contains a custom `TypeHandler` to use with Dapper.`dotnet add package JOS.Enumeration.Database.Dapper`
### JOS.Enumeration.Database.EntityFrameworkCore
Contains ConfigureEnumeration extension method to allow usage with EntityFramework Core.`dotnet add package JOS.Enumeration.Database.EntityFrameworkCore`
## Usage
* Create a new *partial* `record` or `class`
* Implement the `IEnumeration` interface
* Add your Enumeration items
```csharp
public partial record Hamburger : IEnumeration
{
public static readonly Hamburger Cheeseburger = new (1, "Cheeseburger");
public static readonly Hamburger BigMac = new(2, "Big Mac");
public static readonly Hamburger BigTasty = new(3, "Big Tasty");
}
```
The source generator will implement the following interface:
```csharp
// Default implementation -> int as Value
public interface IEnumeration : IEnumeration where T : IEnumeration
{
}public interface IEnumeration where TValue : IConvertible
{
TValue Value { get; }
string Description { get; }
static abstract IReadOnlySet GetAll();
static abstract IEnumerable GetEnumerable();
static abstract TType FromValue(TValue value);
static abstract TType FromDescription(string description);
static abstract TType FromDescription(ReadOnlySpan description);
static abstract Type ValueType { get; }
}
```
The following code will be generated:
```csharp
[System.Diagnostics.DebuggerDisplay("{Description}")]
public partial record Hamburger : IComparable
{
private static readonly IReadOnlySet AllItems;
static Hamburger()
{
AllItems = new HashSet(3)
{
Cheeseburger,
BigMac,
BigTasty
}.ToFrozenSet(optimizeForReading: true);
}private Hamburger(int value, string description)
{
Value = value;
Description = description ?? throw new ArgumentNullException(nameof(description));
}public int Value { get; }
public string Description { get; }
public static IReadOnlySet GetAll()
{
return AllItems;
}public static IEnumerable GetEnumerable()
{
yield return Cheeseburger;
yield return BigMac;
yield return BigTasty;
}public static JOS.Enumerations.Hamburger FromValue(int value)
{
return value switch
{
1 => Cheeseburger,
2 => BigMac,
3 => BigTasty,
_ => throw new InvalidOperationException($"'{value}' is not a valid value in 'JOS.Enumerations.Hamburger'")
};
}public static JOS.Enumerations.Hamburger FromDescription(string description)
{
return description switch
{
"Cheeseburger" => Cheeseburger,
"Big Mac" => BigMac,
"Big Tasty" => BigTasty,
_ => throw new InvalidOperationException($"'{description}' is not a valid description in 'JOS.Enumerations.Hamburger'")
};
}public static JOS.Enumerations.Hamburger FromDescription(ReadOnlySpan description)
{
return description switch
{
"Cheeseburger" => Cheeseburger,
"Big Mac" => BigMac,
"Big Tasty" => BigTasty,
_ => throw new InvalidOperationException($"'{description}' is not a valid description in 'JOS.Enumerations.Hamburger'")
};
}public static Type ValueType => typeof(int);
public int CompareTo(JOS.Enumerations.Hamburger? other) => Value.CompareTo(other!.Value);
public static implicit operator int (JOS.Enumerations.Hamburger item) => item.Value;
public static implicit operator JOS.Enumerations.Hamburger(int value) => FromValue(value);
}
```
## Features
* Generic value
* Generated `IComparable` method.
* Generated implicit operators (convert to/from int).
* Generated optimized `GetAll`, `FromValue` and `FromDescription` methods.
* System.Text.Json support
* Database support (Dapper and EF Core).### Generic value
It's possible to use a generic value instead of the default `int` value by implementing the `IEnumeration` interface.
```csharp
public partial record Car : IEnumeration
{
public static readonly Car FerrariSpider = new("ferrari-spider", "Ferrari Spider");
public static readonly Car TeslaModelY = new("tesla-model-y", "Tesla Model Y");
}
```
`TValue` has a [*IConvertible*](https://learn.microsoft.com/en-us/dotnet/api/system.iconvertible?WT.mc_id=DT-MVP-5004074) constraint.The following types has been tested and are guaranteed to work:
* int (default)
* bool
* decimal
* long
* string
* uint
* ulong### JSON
The package comes with a generic `JsonConverter`. You'll need to register a custom converter for each enumeration.
Example:
```csharp
var jsonSerializerOptions = new JsonSerializerOptions
{
Converters = { new EnumerationJsonConverter() }
};
```
If you're using a custom value, you need to register the converter like this:
```csharp
var jsonSerializerOptions = new JsonSerializerOptions
{
Converters = { new EnumerationJsonConverter() }
};
```
It supports the following scenarios:
* Serializing to `TValue`
* Deserializing from `TValue`If you want any other behaviour, just create your own converter and register it.
### Database
```csharp
public class MyEntity
{
public MyEntity(Guid id, Hamburger hamburger)
{
Id = id;
Hamburger = hamburger;
}public Guid Id { get; }
public Hamburger Hamburger { get; }
}
```
#### Dapper
* Register the TypeHandler: `SqlMapper.AddTypeHandler(new EnumerationTypeHandler())`
* Query like this:
```csharp
var results = (await actConnection.QueryAsync(
"SELECT id, hamburger from my_entities WHERE id = @id", new {id = myEntity.Id})).ToList();
```#### EF Core
* Configure your DB Context
```csharp
public DbSet MyEntities { get; set; } = null!;protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(JosEnumerationDbContext).Assembly);
}
```
```csharp
public class MyEntityEntityTypeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Hamburger).ConfigureEnumeration().IsRequired();
}
}
```
* Query:
```csharp
var result = await myDbContext.MyEntities.FirstAsync(x => x.Id == myEntity.Id);
```
### Primitive Collections
Support for primitive collections in net8.0 can be configured like this:#### EF Core
```csharp
public void Configure(EntityTypeBuilder builder)
{
builder.ConfigureEnumeration(x => x.Cars);
}
```#### Dapper
```csharp
SqlMapper.AddTypeHandler(new EnumerationArrayTypeHandler());
```