3

I need to implement custom Model Validator in .NET Core project. I need to do complex validation so I am avoiding Attribute validation. I used

public class UserModel : IValidatableObject {
    public int UserId { get; set; }
    public string UserName { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {

    }
}

But I need to separate Validation logic from Model So I found this in ASP.NET MVC -

public class ProjectModelValidatorProvider : ModelValidatorProvider {
    public override IEnumerable<ModelValidator> 
        GetValidators(ModelMetadata metadata, ControllerContext context) {
            if (metadata.ModelType == typeof(User))
                yield return new UserModelValidator(metadata, context);
    }
}

public class UserModelValidator : ModelValidator {
    public UserModelValidator(ModelMetadata metadata, ControllerContext controllerContext)
    : base(metadata, controllerContext) { }

    public override IEnumerable<ModelValidationResult> Validate(object container) {
    var model = (User)Metadata.Model;
    if (string.IsNullOrEmpty(model.Login))
        yield return new ModelValidationResult { 
            MemberName = "Login", 
            Message = "Please specify Login" 
        };


    }
}

// register your Provider
ModelValidatorProviders.Providers.Add(new ProjectModelValidatorProvider());

With the help of ModelValidatorProvider and ModelValidator I achieved it. But how to achieve this in .NET Core such that with ModelState.IsValid I will be able to validate the model ?

2 Answers 2

4

In ASP.NET Core , you could customize a class that inherits IModelValidatorProvider interface like below :

public class CustomModelValidatorProvider : IModelValidatorProvider
{
    public void CreateValidators(ModelValidatorProviderContext context)
    {
        if (context.ModelMetadata.ContainerType == typeof(User))
        {
            context.Results.Add(new ValidatorItem
            {
                Validator = new UserModelValidator(),
                IsReusable = true
            });
        }
    }
}

public class UserModelValidator : IModelValidator
{
    private static readonly object _emptyValidationContextInstance = new object();
    public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
    {
        var validationResults = new List<ModelValidationResult>();


        if (validationContext.ModelMetadata.Name == "FirstName" && validationContext.Model == null)
        {
            var validationResult = new ModelValidationResult("", "FirstName is required");

            validationResults.Add(validationResult);

        }
        return validationResults;
    }
}

You could also add partial keyword in your model to separate Validation logic from Model , refer to following code:

public partial class User
{
    public int Id { get; set; }

    public string Gender { get; set; }

    [MaxLength(10)]
    public string FirstName { get; set; }

    [MaxLength(30)]
    public string LastName { get; set; }

    [EmailAddress]
    public string Email { get; set; }

    public List<UserGroups> UserGroups { get; set; }
    public List<Referal> Referals { get; set; }

}

public partial class User : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (string.IsNullOrEmpty(FirstName))
        {
            yield return new ValidationResult("FirstName is Required", new[] { "FirstName" });
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

I am trying to implement 1st suggestion. Is there any way to inject repository inside ModelValidator ? I registered modelvalidatorprovider in services.AddMvc(options => { options.ModelValidatorProviders.Add(new ModelValidatorProvider()); })
@artista_14 services.AddScoped<IModelValidatorProvider, ModelValidatorProvider>(); services.AddMvc(options => { options.ModelValidatorProviders. Add(services.BuildServiceProvider().GetService<IModelValidatorProvider>() ); }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
0

I tried to use the first method.But user class [MaxLength(10)] attribute No more work. The effect I want to achieve is that it can be controlled by attributes.After passing, you can go to custom verification.

2 Comments

if u will use custom validators, attributes will not work. You have to write all validations explicitly.
@artista_14 I tried to read some fields of the ValidationContext class.Found a property that can be read to the attribute.I'm trying to trigger model validation manually.This seems to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.