I’m new to .NET, and I’ve been struggling with using Enum as data annotation for validation in my requests. It took me a full day to solve most of the issues, but I’m not confident that my solution is good practice.
My goal is to implement validation on a FormBody property where the type of that property is an Enum.
Here’s an example of the Enum:
public enum Color
{
Red,
Green,
Blue
}
Now, to simplify the question, I have two models that use this Color. Let’s call them Car and CarDtoRequest.
In the CarDtoRequest, which is used as the type for FromBody, it looks like this:
public class CarDtoRequest
{
[Required]
[EnumDataType(typeof(Color), ErrorMessage = "Color must be either 'Red', 'Green', or 'Blue'")]
public string Color { get; set; }
}
Also, the actual Car type class looks like this:
public class Car
{
[Column(TypeName = "nvarchar(50)")]
public Color Color { get; set; }
}
My first question is, it took me 3-4 hours, and I still can’t assign a dynamic string into the ErrorMessage that joins all the values from the Color (any non-static string shows the error: CS0182: An attribute argument must be a constant expression, typeof expression, or array creation expression of an attribute parameter type).
Additionally, I migrated to the database in the DbContext class as follows:
public DbSet<Car> Cars { get; set; }
The validation works, and it will only work if the FromBody color is one of the enum values. Also, it saves it in the database as a string, but in my code, it still gets saved as a number.
I’m using AutoMapper to convert from the domain model to the DTO model and vice versa.
However, all of this doesn’t seem like good practice and professional. Any suggestions for improvement would be appreciated.
2
Answers
Try a custom Get/Set
You won’t be able to generate the error message dynamically from the caller/user of the attribute, but you can generate the message inside the attribute itself. Here is an example of how that would look like:
Notice how in the constructor, we call into a
private static
method to generate the error message, and then pass that error message to the base constructor.Keep in mind the logic above is just an example and should probably be optimized a bit before using in any critical code path.
When using the attribute like this with a
DayOfWeek
enum, such as:And passing an incorrect value to it:
This will produce the following error message:
Few comments on this implementation:
{0}
in it. When the error message is generated, this placeholder is replaced with the actual property name that is annotated. This is preferred to hardcoding property names or passing generic names in the message.Type
object.Before you commit to this however, I’d suggest considering the FluentValidation framework, which has built-in enum validation that you can more easily tap into.
As for your question regarding handling these enums as text instead of numbers in EntityFrameworkCore, you can achieve this fairly easily by indicating to EF that the enum has a conversion to
string
:Alternatively, you can define this in a dedicated
IEntityTypeConfiguration
implementation of course:And add it to the context: