Search code examples
validationselectrazorasp.net-core-mvcdata-annotations

asp.net core select validation with data annotation not working


I have spent a remarkable amount of time to find out but I couldn't. When I post my model without selecting any option - in fact selecting 0 - Select - option, the Required validation is not working.

I also tried removing the programatically added default option from service code to the view code, validation has not worked like this as well.

If I removed the default options completely, then view engine automatically selects the first option from the list and the validation is never be done.

How can I make server side validation done properly?

This is my model

public class AuditViewModel
{
    public Guid Id { get; set; }

    [Display(Name = "Subject")]
    [Required(ErrorMessage = "IsRequired")]
    public string Subject { get; set; }

    public string AuditType { get; set; }

    public string LocationCountry { get; set; }

    public string LocationOffice { get; set; }

    [Required(ErrorMessage = "IsRequired")]
    [Display(Name = "AuditType")]
    public int AuditTypeId { get; set; }

    public string CreatedOn { get; set; }

    public string ModifiedOn { get; set; }

    public string CreatedBy { get; set; }

    [Display(Name = "Description")]
    [Required(ErrorMessage = "IsRequired")]
    public string Description { get; set; }

    [Display(Name = "Country")]
    [Required(ErrorMessage = "IsRequired")]
    public int LocationCountryId { get; set; }

    [Display(Name = "Office")]
    [Required(ErrorMessage = "IsRequired")]
    public int LocationOfficeId { get; set; }

    [Display(Name = "Season")]
    public string Season { get; set; }

    public List<SelectListItem> Countries { get; set; }

    public List<SelectListItem> AuditTypes { get; set; }

    public List<SelectListItem> Offices { get; set; }

    public List<AuditViewModel> AuditList { get; set; }
}

This is the service that get select list data and creates the lists for binding

public class AuditViewModelService : IAuditViewModelService
{
    public List<SelectListItem> GetAuditTypes()
    {
        var list = new List<SelectListItem>
            {
                new SelectListItem { Text = _sharedLocalizer.GetLocalizedString("Select"), Value = "0", Selected = true }
            };
        foreach (AuditType item in Enum.GetValues(typeof(AuditType)))
        {
            list.Add(new SelectListItem { Text = _enumLocalizer.GetLocalizedString(item.ToString()), Value = ((int)item).ToString() });
        }
        return list;
    }

    public List<SelectListItem> GetCountries()
    {
        var list = new List<SelectListItem> { new SelectListItem { Text = _sharedLocalizer.GetLocalizedString("Select"), Value = "0", Selected = true } };
        list.AddRange(_countryRepository.GetAll().ToList().ToSelectListItemList("Name"));
        return list;
    }

    public List<SelectListItem> GetOffices()
    {
        var list = new List<SelectListItem> { new SelectListItem { Text = _sharedLocalizer.GetLocalizedString("Select"), Value = "0", Selected = true } };
        list.AddRange(_officeRepository.GetAll().ToList().ToSelectListItemList("Name"));
        return list;
    }
}

An this is select inputs part of the view

<div class="form-group m-form__group">
    <label asp-for="AuditTypeId"></label>
    <select asp-for="AuditTypeId" asp-items="@Model.AuditTypes" class="form-control m-input m-input--square" id="auditTypeId">
        <option value="0">Select</option>
    </select>
    <span asp-validation-for="AuditTypeId"></span>
</div>
<div class="form-group m-form__group">
    <label asp-for="LocationCountryId"></label>
    <select asp-for="LocationCountryId" asp-items="@Model.Countries" class="form-control m-input m-input--square" id="locationCountryId">
        <option value="0">Select</option>
    </select>
    <span asp-validation-for="LocationCountryId"></span>
</div>
<div class="form-group m-form__group">
    <label asp-for="LocationOfficeId"></label>
    <select asp-for="LocationOfficeId" asp-items="@Model.Offices" class="form-control m-input m-input--square" id="locationOfficeId">
        <option value="0">Select</option>
    </select>
    <span asp-validation-for="LocationOfficeId"></span>
</div>

Solution

  • from RequiredAttribute

    The RequiredAttribute attribute specifies that when a field on a form is validated, the field must contain a value. A validation exception is raised if the property is null, contains an empty string (""), or contains only white-space characters.

    Now look at your model properties

    public int LocationCountryId { get; set; }

    public int AuditTypeId { get; set; }

    public int LocationOfficeId { get; set; }

    int cannot be null, cannot contain an empty string, and cannot take white-space therefore Required in this context will pass validation every single time. default(int) will return 0 so you will notice that these properties are 0 on postback.

    Either you need to change these to int? so your property can be in a null state or you need to use the Range attribute and do something like Range(1, int.MaxValue) so you can target the 0 value for the error message.