JOS.Enumeration
                             
                            
                                2.0.5-beta-g4df623a3fd
                            
                        
                    See the version list below for details.
dotnet add package JOS.Enumeration --version 2.0.5-beta-g4df623a3fd
NuGet\Install-Package JOS.Enumeration -Version 2.0.5-beta-g4df623a3fd
<PackageReference Include="JOS.Enumeration" Version="2.0.5-beta-g4df623a3fd" />
<PackageVersion Include="JOS.Enumeration" Version="2.0.5-beta-g4df623a3fd" />
<PackageReference Include="JOS.Enumeration" />
paket add JOS.Enumeration --version 2.0.5-beta-g4df623a3fd
#r "nuget: JOS.Enumeration, 2.0.5-beta-g4df623a3fd"
#:package JOS.Enumeration@2.0.5-beta-g4df623a3fd
#addin nuget:?package=JOS.Enumeration&version=2.0.5-beta-g4df623a3fd&prerelease
#tool nuget:?package=JOS.Enumeration&version=2.0.5-beta-g4df623a3fd&prerelease
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 recordorclass
- Implement the IEnumeration<T>interface
- Add your Enumeration items
public partial record Hamburger : IEnumeration<Hamburger>
{
    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:
// Default implementation -> int as Value
public interface IEnumeration<T> : IEnumeration<int, T> where T : IEnumeration<T>
{
}
public interface IEnumeration<TValue, TType> where TValue : IConvertible
{
    TValue Value { get; }
    string Description { get; }
    static abstract IReadOnlySet<TType> GetAll();
    static abstract IEnumerable<TType> GetEnumerable();
    static abstract TType FromValue(TValue value);
    static abstract TType FromDescription(string description);
    static abstract TType FromDescription(ReadOnlySpan<char> description);
    static abstract Type ValueType { get; }
}
The following code will be generated:
[System.Diagnostics.DebuggerDisplay("{Description}")]
public partial record Hamburger : IComparable<JOS.Enumerations.Hamburger>
{
    private static readonly IReadOnlySet<JOS.Enumerations.Hamburger> AllItems;
    
    static Hamburger()
    {
        AllItems = new HashSet<JOS.Enumerations.Hamburger>(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<JOS.Enumerations.Hamburger> GetAll()
    {
        return AllItems;
    }
    public static IEnumerable<JOS.Enumerations.Hamburger> 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<char> 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<T>method.
- Generated implicit operators (convert to/from int).
- Generated optimized GetAll,FromValueandFromDescriptionmethods.
- 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<TValue, TEnumeration> interface.
public partial record Car : IEnumeration<string, Car>
{
    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 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:
var jsonSerializerOptions = new JsonSerializerOptions
{
    Converters = { new EnumerationJsonConverter<Hamburger>() }
};
If you're using a custom value, you need to register the converter like this:
var jsonSerializerOptions = new JsonSerializerOptions
{
    Converters = { new EnumerationJsonConverter<string, Car>() }
};
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
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<Hamburger>())
- Query like this:
var results = (await actConnection.QueryAsync<MyEntity>(
            "SELECT id, hamburger from my_entities WHERE id = @id", new {id = myEntity.Id})).ToList(); 
EF Core
- Configure your DB Context
public DbSet<MyEntity> MyEntities { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfigurationsFromAssembly(typeof(JosEnumerationDbContext).Assembly);
} 
public class MyEntityEntityTypeConfiguration : IEntityTypeConfiguration<MyEntity>
{
    public void Configure(EntityTypeBuilder<MyEntity> builder)
    {
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Hamburger).ConfigureEnumeration().IsRequired();
    }
}
- Query:
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
public void Configure(EntityTypeBuilder<MyEntity> builder)
{
    builder.ConfigureEnumeration<MyEntity, string, Car>(x => x.Cars);
}
Dapper
SqlMapper.AddTypeHandler(new EnumerationArrayTypeHandler<string, Car>());
| Product | Versions Compatible and additional computed target framework versions. | 
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. | 
- 
                                                    net8.0- No dependencies.
 
- 
                                                    net9.0- No dependencies.
 
NuGet packages (2)
Showing the top 2 NuGet packages that depend on JOS.Enumeration:
| Package | Downloads | 
|---|---|
| JOS.Enumeration.Database.EntityFrameworkCore Package Description | |
| JOS.Enumeration.Database.Dapper Package Description | 
GitHub repositories
This package is not used by any popular GitHub repositories.