Enum class type converters
Everybody loves class enums, right? Maybe not, but how do you use them when you want to map to settings read from configuration? You have a .Net app which reads JSON settings from appSettings.json
and one of the settings needs to be mapped to an enumeration. But instead of an enum
you would like to use an enumeration class. How do you do it? Type converters to the rescue.
Let’s say we have this appSettings.json
:
{
"MyOptions": {
"MyEnumValue": "enumTypeValue1"
}
}
, and this enumeration class we try to map our MyEnumValue
to:
public class MyEnumValue
{
public static MyEnumValue EnumTypeValue1 = new("enumTypeValue1");
public string Name { get; set; }
public MyEnumValue(string name) => Name = name;
public static implicit operator string(MyEnumValue enumValue) => enumValue.Name;
}
Note: usually these type of classes should implement
IComparable
orIEquatable<T>
- removed for brevity.
The implicit operator is used just to be able to use string
and MyEnumValue
interchangeably and has no other relevance to the case being described here.
The options class used to read the whole settings object:
public class MyClass
{
private readonly MyOptions _options;
public MyClass(IOptions<MyOptions> options)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
public string GetValue() => _options.MyEnumValue;
}
To be complete, this is the simplest way to build your app host to automatically read your app settings json and to register it for dependency injection via an IOptions<T>
type:
static void Main(string[] args)
{
using IHost host = CreateHostBuilder(args).Build();
var myClass = host.Services.GetService<MyClass>();
Console.WriteLine(myClass.GetValue());
}
private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
var configuration = context.Configuration;
services.Configure<MyOptions>(configuration.GetSection(nameof(MyOptions)));
services.AddSingleton<MyClass>();
});
Note that
Host.CreateDefaultBuilder
will automatically load appIConfiguration
fromappSettings.json
If you would run your console application now, it would fail because the string value of enumTypeValue1
cannot be mapped to MyEnumValue.EnumTypeValue1
and the _options.MyEnumValue
will be null.
By now you would already have guessed that the missing piece of the puzzle is the type converter:
public class MyEnumValueTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string strValue)
return new MyEnumValue(strValue);
return base.ConvertFrom(context, culture, value);
}
And now, if we annotate our enum value class with the [TypeConverter(typeof(MyEnumValueTypeConverter))]
attribute our app will work just fine.
This is it, the easy-peasy way of using enumeration classes mapped to appSettings.json
options values. You can download a full working example from github