Ardalis.SmartEnum

repository·main·Indexed 25 days ago

https://github.com/ardalis/smartenum

A type-safe, object-oriented alternative to standard C# enums that allows developers to attach data and behavior to enum members. It supports custom value types, inheritance for polymorphic behavior, and bitwise flags via SmartFlagEnum. The library includes specialized sub-packages for integration with EF Core, Dapper, AutoFixture, and various serialization formats including System.Text.Json, Json.NET, MessagePack, ProtoBufNet, and Utf8Json.

Tokens
5.8K
Snippets
17
Records
23
Agent score
32%

What's inside Ardalis.SmartEnum

  1. Implement bitwise flags with SmartFlagEnum

    main

    For flag-like behavior, inherit from SmartFlagEnum<TEnum>.

    Requirements:

    • Values must be powers of two (1, 2, 4, 8, etc.). If they are not, a SmartFlagEnumDoesNotContainPowerOfTwoValuesException is thrown. You can bypass this with [AllowUnsafeFlagEnumValues], but the enum may not behave as expected.
    • You can define explicit Combination values (e.g., CardAndCash = 3) which will be returned by FromValue() instead of the individual flags.
    • A value of -1 passed to FromValue() returns an IEnumerable containing all values (excluding 0).

    Key Methods:

    • FromValue(TValue value): Returns an IEnumerable<TEnum> containing all matching flags.
    • FromName(string names): Accepts a comma-separated string of names and returns an IEnumerable<TEnum>.
    • FromValueToString(TValue value): Returns a comma-separated string of the names of the matching flags.
    public class SmartFlagTestEnum : SmartFlagEnum<SmartFlagTestEnum>
    {
        public static readonly SmartFlagTestEnum None = new SmartFlagTestEnum(nameof(None), 0);
        public static readonly SmartFlagTestEnum Card = new SmartFlagTestEnum(nameof(Card), 1);
        public static readonly SmartFlagTestEnum Cash = new SmartFlagTestEnum(nameof(Cash), 2);
        public static readonly SmartFlagTestEnum CardAndCash = new SmartFlagTestEnum(nameof(CardAndCash), 3);
        public static readonly SmartFlagTestEnum Bpay = new SmartFlagTestEnum(nameof(Bpay), 4);
    
        public SmartFlagTestEnum(string name, int value) : base(name, value)
        {
        }
    }
    
    // Usage
    var result = SmartFlagTestEnum.FromValue(3); // Returns Card and Cash
    var names = SmartFlagTestEnum.FromValueToString(3); // "Card, Cash"
  2. Add behavior to a SmartEnum using inheritance

    main

    Unlike standard enums, SmartEnums can use inheritance to attach properties or methods to specific enum members. This allows you to replace switch statements with polymorphic behavior. You define an abstract base class and private sealed classes for each enum member that override the abstract members.

    using Ardalis.SmartEnum;
    
    public abstract class EmployeeType : SmartEnum<EmployeeType>
    {
        public static readonly EmployeeType Manager = new ManagerType();
        public static readonly EmployeeType Assistant = new AssistantType();
    
        private EmployeeType(string name, int value) : base(name, value)
        {
        }
    
        public abstract decimal BonusSize { get; }
    
        private sealed class ManagerType : EmployeeType
        {
            public ManagerType() : base("Manager", 1) {}
    
            public override decimal BonusSize => 10_000m;
        }
    
        private sealed class AssistantType : EmployeeType
        {
            public AssistantType() : base("Assistant", 2) {}
    
            public override decimal BonusSize => 1_000m;
        }
    }
  3. Configure AutoFixture support for SmartEnum

    main

    By default, AutoFixture attempts to create new instances of types. Since SmartEnum instances should always be references to existing ones, use the Ardalis.SmartEnum.AutoFixture package. You can integrate it by adding the SmartEnumCustomization to your IFixture builder.

    var fixture = new Fixture()
        .Customize(new SmartEnumCustomization());
    
    var smartEnum = fixture.Create<TestEnum>();
  4. Define a SmartEnum with a custom value type

    main

    You can specify a different type for the enum's value (e.g., ushort, long, string) by providing a second generic argument to SmartEnum<TEnum, TValue>.

    using Ardalis.SmartEnum;
    
    public sealed class TestEnum : SmartEnum<TestEnum, ushort>
    {
        public static readonly TestEnum One = new TestEnum("A string!", 1);
        public static readonly TestEnum Two = new TestEnum("Another string!", 2);
        public static readonly TestEnum Three = new TestEnum("Yet another string!", 3);
    
        private TestEnum(string name, ushort value) : base(name, value)
        {
        }
    }
  5. Enable Dapper support for SmartEnum

    main

    To allow Dapper to map SmartEnum values to database columns, you must register a type handler with SqlMapper.

    • Use SmartEnumByNameTypeHandler<T> to map the Name of the enum to a database column.
    • Use SmartEnumByValueTypeHandler<T> to map the Value of the enum to a database column.
    // Maps the name of TestEnum objects (e.g. "One", "Two", or "Three") to a database column.
    SqlMapper.AddTypeHandler(typeof(TestEnum), new SmartEnumByNameTypeHandler<TestEnum>());
    // Maps the value of TestEnum objects (e.g. 1, 2, or 3) to a database column.
    SqlMapper.AddTypeHandler(typeof(TestEnum), new SmartEnumByValueTypeHandler<TestEnum>());
  6. Install Ardalis.SmartEnum via NuGet

    main

    To use SmartEnum, install the base package via NuGet. Depending on your requirements for serialization or ORM support, you may also need to install specific sub-packages.

    Base Package:

    Install-Package Ardalis.SmartEnum

    Additional Support Packages:

    • Ardalis.SmartEnum.AutoFixture
    • Ardalis.SmartEnum.JsonNet
    • Ardalis.SmartEnum.SystemTextJson
    • Ardalis.SmartEnum.Utf8Json
    • Ardalis.SmartEnum.MessagePack
    • Ardalis.SmartEnum.ProtoBufNet
    • Ardalis.SmartEnum.EFCore
    • Ardalis.SmartEnum.ModelBinding
    • Ardalis.SmartEnum.Dapper
    Install-Package Ardalis.SmartEnum
  7. Persist SmartEnum with EF Core

    main

    To persist SmartEnums in EF Core, you must map the enum to its underlying value type using value conversions.

    Manual Configuration (EF Core 2.1+)

    Use HasConversion in OnModelCreating to map the property to its Value and use FromValue to reconstruct it.

    Note: You must implement a parameterless constructor in your SmartEnum class for EF Core to work correctly.

    builder.Entity<Policy>()
        .Property(p => p.PolicyStatus)
        .HasConversion(
            p => p.Value,
            p => PolicyStatus.FromValue(p));

    Automatic Configuration (using Ardalis.SmartEnum.EFCore)

    If you have the Ardalis.SmartEnum.EFCore package installed, you can use pre-convention configuration.

    EF Core 6+:

    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.ConfigureSmartEnum();
    }

    EF Core < 6:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ConfigureSmartEnum();
    }
    // EF Core 6+ example
    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.ConfigureSmartEnum();
    }
  8. Define a basic SmartEnum

    main

    To create a smart enum, inherit from SmartEnum<TEnum>. You must define static readonly instances of your enum and provide a private constructor that calls the base constructor with a name and a value. By default, the value type is int.

    using Ardalis.SmartEnum;
    
    public sealed class TestEnum : SmartEnum<TestEnum>
    {
        public static readonly TestEnum One = new TestEnum(nameof(One), 1);
        public static readonly TestEnum Two = new TestEnum(nameof(Two), 2);
        public static readonly TestEnum Three = new TestEnum(nameof(Three), 3);
    
        private TestEnum(string name, int value) : base(name, value)
        {
        }
    }
  9. Configure System.Text.Json serialization for SmartEnum

    main

    To control whether a SmartEnum is serialized using its Name or its Value in System.Text.Json, use the Ardalis.SmartEnum.SystemTextJson package. Apply the appropriate converter attribute to the property in your model.

    • Use SmartEnumNameConverter<TEnum, TValue> to serialize/deserialize using the Name.
    • Use SmartEnumValueConverter<TEnum, TValue> to serialize/deserialize using the Value.
    public class TestClass
    {
        [JsonConverter(typeof(SmartEnumNameConverter<TestEnum,int>))]
        public TestEnum Property { get; set; }
    }
  10. Configure Json.NET serialization for SmartEnum

    main
    To control whether a SmartEnum is serialized using its Name or its Value in Json.NET, use the Ardalis.SmartEnum.JsonNet package. Apply the appropriate converter attribute to the property in your model.