My technique is simliar to neeohw's but lets me use real enums. And it's generic so I don't have to write it many times.
There's an immutable struct that wraps the enum value. It has a single property and implicit conversions, plus a generic custom type handler.
public readonly struct DapperableEnum<TEnum> where TEnum : Enum
{
[JsonConverter(typeof(StringEnumConverter))]
public TEnum Value { get; }
static DapperableEnum()
{
Dapper.SqlMapper.AddTypeHandler(typeof(DapperableEnum<TEnum>), new DapperableEnumHandler<TEnum>());
}
public DapperableEnum(TEnum value)
{
Value = value;
}
public DapperableEnum(string description)
{
Value = EnumExtensions.GetValueByDescription<TEnum>(description);
}
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.ITypeHandler
where TEnum : Enum
{
public object Parse(Type destinationType, object value)
{
if (destinationType == typeof(DapperableEnum<TEnum>))
{
return new DapperableEnum<TEnum>((string)value);
}
throw new InvalidCastException($"Can't parse string value {value} into enum type {typeof(TEnum).Name}");
}
public void SetValue(IDbDataParameter parameter, object value)
{
parameter.DbType = DbType.String;
parameter.Value =((DapperableEnum<TEnum>)value).Value.GetDescription();
}
}
I use the static constructor to automatically register the type handler at startup.
I use GetDescription / GetValueByDescription (same idea as this answer) to support strings that wouldn't be valid C# enum values. If you don't need this feature, ToString and Enum.Parse will work fine.
The JsonConverter attribute makes Json.Net use string values too. Of course remove it if you don't use Json.Net
Here's an example:
enum Holiday
{
Thanksgiving,
Christmas,
[Description("Martin Luther King, Jr.'s Birthday")]
MlkDay,
Other,
}
class HolidayScheduleItem : IStandardDaoEntity<HolidayScheduleItem>
{
public DapperableEnum<Holiday> Holiday {get; set;}
public DateTime When {get; set;}
}
And calling code can use the normal enum values.
var item = new HolidayScheduleItem()
{
Holiday = Holiday.MlkDay,
When = new DateTime(2021, 1, 18)
};
It works with plain Dapper or Dapper.Contrib:
await conn.ExecuteAsync("INSERT HolidayScheduleItem ([Holiday], [When])
VALUES(@Holiday, @When)", item);
await conn.InsertAsync(item);
