Return all values from enum via API in .NET

on

I would love for the people implementing my API to send me a value from my enumerator as an input parameter. But, I’d also love it if they could somehow access all possible values of that enumerator in some other way other than reading the documentation (because, let’s be honest, who reads the documentation anyway, right?).

While we could go into the whole “don’t use enums, use lookup tables or static strings….” let’s not go there in this post. In this post I will explain how I dealt with the problem of returning all possible values from an enum type with the integer key and a descriptive name that could be shown to the users.

The code to follow along with the blog post is available on Github.

Setting up the problem

So, I have an enum:

public enum DogTypesEnum
{
    GERMAN_SHEPHERD = 1,
    LABRADOR_RETREIVER = 2,
    SIBERIAN_HUSKEY = 3    
}

I would like to return a nice JSON object from my API looking something like this:

[
  {
    'key': 1,
    'name': 'German shepherd'
  },
  {
    'key': 2,
    'name': 'Labrador retriever'
  },
  {
    'key': 3,
    'name': 'Siberian huskey'
  }
]

However, I have several different enum types in my code, so I’d like to do this as universally as possible.

Get all possible values from enum type

So the first thing we need to do is to get all possible values from an enum:

var allPossibleValues = Enum.GetValues(typeof(DogTypeEnum)).Cast<DogTypeEnum>()

OK – that was not too hard. Now we need to make this universal – I’d love to be able to get it for any enum type. This is where the trickery starts. You cannot extend System.Enum to add your own extension methods.

But, we can make a static class like this one:

public class Enum<T> where T : Enum
{

}

Do note that this is not actually extending the System.Enum class, but adding a completely new class of type Enum<T>. Technically it could be called EnumHelper<T> or something to make things clearer. This is an idea I found on StackOverflow (thanks to ShawnFeatherly).

So, now that we have this, we can just add our method that gets all possible types in there:

public class Enum<T> where T : Enum
{
    public static IEnumerable<T> GetAllValuesAsIEnumerable()
    {
        return Enum.GetValues(typeof(T)).Cast<T>();
    }
}

Even though, as we already mentioned, this is not a true extension of the System.Enum, it is close and does the trick quite nicely.

Now I can do something like:

var allPossibleValues = Enum<DogTypeEnum>.GetAllValuesAsIEnumerable();

Mapping enums to DTOs

I have a universal DTO for any enum type:

public class EnumDTO
{
    public int Key { get { return Convert.ToInt32(_enum); } }
    public string Name { get { return _enum.ToString(); } }

    private Enum _enum;

    public EnumDTO(Enum inputEnum)
    {
        _enum = inputEnum;
    }
}

I like to keep a reference to the initial enum from which this DTO was created – not that it matters, but if I ever need it in the future, I can always extend this to get it and use it.

So, to get an actual list that can be returned via an API we can do this:

var enumDTOs = Enum<DogTypeEnum>.GetAllValuesAsIEnumerable().Select(d => new EnumDTO(d));

By returning enumDTOs from the API controller we would get an output similar to what was initially intended. However, there is one single problem. The value of our Name property is “GERMAN_SHEPHERD”, and we’d want it to be something more user friendly, like e.g. “German shepherd”.

Formatting enum string value

As I said, I’d love it if this could be universal. So I’d love it if I could manipulate the ToString() method of System.Enum. However, as I already mentioned, this is not possible.

So, a possible approach is to annotate your enum values with an attribute that can then hold the desired value and then just use that. It is possible to create your own, but I am using the Description attribute that already exists in the System.ComponentModel namespace.

public enum DogTypesEnum
{
    [Description("German shepherd")]
    GERMAN_SHEPHERD = 1,

    [Description("Labrador retreiver")]
    LABRADOR_RETREIVER = 2,

    [Description("Siberian huskey")]
    SIBERIAN_HUSKEY = 3    
}

We then just need a method that will then read this Description parameter when we need it. We cannot change the ToString() method of Enum, unfortunately. But, we can extend any Enum with a ToDescription() method that would then return this description:

public static class AttributeHelperExtension
{
    public static string ToDescription(this Enum value)
    {
        var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return da.Length > 0 ? da[0].Description : value.ToString();
    }
}

Now we can do something like this:

var dogType = DogTypeEnum.GERMAN_SHEPHERD;
var dogTypeName = dogType.ToDescription();   // value will be "German shepherd"

After this, we can just change the definition of the Name property in the EnumDTO class:

public string Name { get { return _enum.ToDescription(); } }

After implementing this last change our output JSON looks indeed like we intended it to.

Conclusion

Are Enums a good thing to use? Should all of this been a static class with properties? Should all of this been a lookup table in the database? All of these are valid points and deserve a discussion over a few rounds of beer. (Incidentally, if you like discussions like these, please contact me so we can have a few rounds of beer).

But, in different codebases you’ll still find enums. And the case described here is not an unusual one. A lot of specifics of this particular case are discussed in length on StackOverflow and other developer sites. However, I did feel a need to show my take on the whole process, top to bottom.

2 thoughts on “Return all values from enum via API in .NET

  1. Hey Mario, it seems you missed the part about API. Would be great to add example of the controller with actions: GET enum as int/string/description. Also would be great to see the route constraint for enum when you have smth like GET Route(“products/{category:myEnum}”)]

    1. Dmitry, thanks for your comment! And thanks for the message later where you provide additional resources about your question 🙂

      So, there is a great article by Nick Heath on his blog about adding Enums as route parameters:
      https://nickheath.net/2019/02/20/asp-net-core-enum-route-constraints

      Dmitry, you’re really awesome for sending this over, it is a great read!

      I might be incorporating some of these tips into my article as well at some point, but for now this should do 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

You are currently offline