Search code examples
c#asp.net-coremodel-view-controllermodel-validation

Issue with IModelValidatorProvider and IModelValidator implementations. The generated error is not treated correctly


I try to use IModelValidatorProvider and IModelValidator interfaces in my ASP.NET Core project for respective model validation.

Unfortunately it does not work as expected. Namely my ModelValidator class (implements IModelValidator) returns only one error. As a result I see that ModelState contains one error with the expected text. It is fine. ModelState.ErrorsCount = 1. But the problem is new element is presented in ModelState.Values. It has a key Code.Code instead of Code where Code is name of one of the model property. That is why the required text is not shown in validation field for Code property on page view. The situation is different if I use ModelState.AddModelError method in the controller.

ModelState.AddModelError("Code", "something went wrong...");

ModelState.Values does not contain additional element with key Code.Code. It contains element with key Code (ValidationState=Invalid) and a error message is shown correctly.

I guess I am doing something wrong with model validators. But I am stuck and can't find the exact issue. Would be thankful for any advice.

Please find below the source code of my validators

public class CustomModelValidatorProvider : IModelValidatorProvider
{

    public CustomModelValidatorProvider()
    {
    }

    public void CreateValidators(ModelValidatorProviderContext context)
    {
        if (context.Results.Any(v => v.Validator.GetType() == typeof(MyCustomModelValidator)) == true)
        {
            return;
        }

        if (context.ModelMetadata.ContainerType == typeof(DealRegistrationViewModel))
        {
            context.Results.Add(new ValidatorItem
            {
                Validator = new MyCustomModelValidator(),
                IsReusable = true
            });
        }
    }
}

public class MyCustomModelValidator : IModelValidator
{
    public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
    {
        var model = context.Container as DealRegistrationViewModel;

        if (context.ModelMetadata.ModelType == typeof(string) 
                && context.ModelMetadata.Name == nameof(model.Code))
        {
            if (string.IsNullOrEmpty(model.Code) == true)
            {
                return new List<ModelValidationResult>
                {
                    new ModelValidationResult(context.ModelMetadata.PropertyName, "Empty Code value is not accepted")
                };
            }
        }

        return Enumerable.Empty<ModelValidationResult>();
    }
}

The validator is activated in startup.cs like this

services.AddMvc(options =>
{
    options.ModelValidatorProviders.Add(new CustomModelValidatorProvider());
});

Please find below with screenshot with ModelState.Values content in case of ModelState.AddModelError usage (works fine)

enter image description here

Please find below with screenshot with ModelState.Values content in case of Model validators usage (it does not work fine) enter image description here


Solution

  • You just need to change your code in your MyCustomModelValidatorlike below:

    if (string.IsNullOrEmpty(model.Code) == true)
                {
                    return new List<ModelValidationResult>
                    {
                       new ModelValidationResult("", "Empty Code value is not accepted")
                    };
                       
                }
    

    Test result: enter image description here