using Microsoft.Data.Sqlite;
using System.ComponentModel;
using System.Collections.Generic;
public static void Main()
using (var connection = new SqliteConnection("Data Source=:memory:"))
connection.Query<TestModel>("SELECT 'ONE' TestEnumString UNION ALL SELECT 'TWO' UNION ALL SELECT NULL UNION ALL SELECT 'Other' UNION ALL SELECT 'NotExisting'").Dump();
EnumExtensions.EnumDescriptionCache<TestEnum>.FailOnNotExisting = true;
connection.Query<TestModel>("SELECT 'ONE' TestEnumString UNION ALL SELECT 'TWO' UNION ALL SELECT NULL UNION ALL SELECT 'Other' UNION ALL SELECT 'NotExisting'").Dump();
internal DapperableEnum<TestEnum> TestEnumString { get; set;}
public TestEnum? TestEnum => TestEnumString.Value;
public readonly struct DapperableEnum<TEnum> where TEnum : struct, Enum
public TEnum? Value { get; }
SqlMapper.AddTypeHandler(new DapperableEnumHandler<TEnum>());
public DapperableEnum(TEnum value)
public DapperableEnum(string description)
Value = description.GetEnumValueFromDescription<TEnum>();
public static implicit operator DapperableEnum<TEnum>(TEnum v) => new DapperableEnum<TEnum>(v);
public static implicit operator TEnum?(DapperableEnum<TEnum> v) => v.Value;
public static implicit operator DapperableEnum<TEnum>(string s) => new DapperableEnum<TEnum>(s);
public class DapperableEnumHandler<TEnum> : SqlMapper.TypeHandler<DapperableEnum<TEnum>> where TEnum : struct, Enum
public override DapperableEnum<TEnum> Parse(object value) => value is DBNull ? null : new DapperableEnum<TEnum>((string)value);
public override void SetValue(IDbDataParameter parameter, DapperableEnum<TEnum> value)
parameter.DbType = DbType.String;
parameter.Value = value.Value.GetDescriptionFromEnumValue();
public static class EnumExtensions
public static TEnum? GetEnumValueFromDescription<TEnum>(this string description) where TEnum : struct, Enum
=> EnumDescriptionCache<TEnum>.Values.TryGetValue(description, out var value) ? value : !EnumDescriptionCache<TEnum>.FailOnNotExisting ? null : throw new ArgumentException("Not found.", nameof(description));
public static string GetDescriptionFromEnumValue<TEnum>(this TEnum? value) where TEnum : struct, Enum
=> value.HasValue ? EnumDescriptionCache<TEnum>.Descriptions[value.Value] : null;
public static class EnumDescriptionCache<TEnum> where TEnum : struct, Enum
static EnumDescriptionCache()
var fields = typeof(TEnum).GetFields().Where(field => field.IsLiteral);
((TEnum)field.GetValue(null), field.GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute ? attribute.Description : field.Name))
.ToDictionary(keyValue => keyValue.Item1, keyValue => keyValue.Item2);
Values = Descriptions.ToDictionary(keyValue => keyValue.Value, keyValue => keyValue.Key);
public static readonly Dictionary<TEnum, string> Descriptions;
public static readonly Dictionary<string, TEnum> Values;
public static bool FailOnNotExisting { get; set; }