I am developing a website built on EntityFrameworkCore and targeting ASP.NET Core 2.1. I want to specify an error message for an enum field in my model like so:
[Required(ErrorMessage = "Select an item from the list.")]
public MyEnum MyEnum { get; set; }
However, the stock message is still generated: The value '0' is invalid. The problem appears to be that the Enum type is validated prior to any of my code being evaluated. The two approaches presented here (https://www.codeproject.com/Articles/1204077/ASP-NET-Core-MVC-Model-Validation), either creating a class that inherits from ValidationAttribute, or having the model inherit from IValidatableObject both suffer from this.
I have found a workaround: declare the field as an int, and then use a custom validation attribute:
[EnumCheck(typeof(MyEnum), ErrorMessage = "Select an item form the list.")]
public int MyEnum { get; set; }
...and then subclass from ValidationAttribute:
sealed public class EnumCheck : ValidationAttribute
{
readonly Type t_;
public EnumCheck(Type t)
{
t_ = t;
}
public override bool IsValid(object value)
{
return Enum.IsDefined(t_, value);
}
}
This approach has some drawbacks as now I need to cast the field to the Enum type in many places that it is used.
Is there a way to provide an ErrorMessage for Enum field types?
UPDATE
The following is a minimal example (No longer using EnumCheck subclass from ValidationAttribute, but rather the EnumDataType mentioned by @PéterCsajtai):
Model
namespace web.Models
{
public enum Day
{
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
public class Form
{
[EnumDataType(typeof(Day), ErrorMessage = "Select an item from the list.")]
public Day Day { get; set; }
}
}
Controller
namespace web.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Save(Form model)
{
if(!ModelState.IsValid)
{
return View("Index");
}
return View("Index", model);
}
}
}
View
<form asp-controller="Home">
<div asp-validation-summary="All" class="text-danger"></div>
<fieldset>
<label asp-for="@Model.Day"></label>
<select asp-for="@Model.Day" asp-items="Html.GetEnumSelectList<Day>()">
<option value="">Select...</option>
</select>
@Html.ValidationMessageFor(m => m.Day)
<span asp-validation-for="@Model.Day" class="text-danger"></span>
</fieldset>
<fieldset>
<input type="submit" asp-action="Save" />
</fieldset>
</form>
And the output after form post:

In your original case,
[Required(ErrorMessage = "Select an item from the list.")]you are setting the message to be shown ifMyEnumis missing. But like all ValueTypes, it can never be missing so it will never trigger that validation. The solution for this is the nullable ValueType.Your second effort still doesn't work because the model-binding failure – "Can a blank value be converted to a
Day? No it can't." kicks in before your validation can kick in.Validation for a
Typepresupposes that you have aninstanceof thatTypeto validate. The way Aspnetcore turns a form post into that typed value, is modelbinding. If the posted value can't be model-bound -- for instance if you post "boo" to a property declared as anint, or an empty string to anEnum-- then validation never even starts. Instead the modelbinding error is shown.The simple solution is
Day?so that modelbinding a blank succeeds (blank resolves tonull).[Required()]so that thatnullvalue then fails validation.Conclusion: change your form to:
And then it will work as you'd expect.
Reference: Model Validation in AspNet Core MVC
NB unlike other
ValidationAttributes, the documentation forEnumDataType, although it inherits from ValidationAttribute, doesn't give an example of using it for validation. Instead the example is of using it for MetaData.