I recently started Implementing custom validation using IValidatableObject in my DOT NET CORE application.
We often use data annotation for simple validation for the properties in our model class. I was wondering how to validate a particular property against some data in the database. I’m happy to know that IValidatableObject can be used to do any sort of validation.
In this post, I’m going to show how to make use of IValidatableObject to validate a property for different scenarios.
I have used DOT NET CORE Web API for this demo.
I have created a folder named, Validation and created an abstract class named AbstractValidatableObject. This class inherits IValidatableObject class and we can implement a common logic to asyn and async Validate methods as shown below.
public abstract class AbstractValidatableObject : IValidatableObject { public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { CancellationTokenSource source = new CancellationTokenSource(); var task = ValidateAsync(validationContext, source.Token); Task.WaitAll(task); return task.Result; } public virtual Task<IEnumerable<ValidationResult>> ValidateAsync(ValidationContext validationContext, CancellationToken cancellation) { return Task.FromResult((IEnumerable<ValidationResult>)new List<ValidationResult>()); } }
IValidatableObject Interface provides a way for an object to be invalidated.
It has a method named Validate(ValidationContext) that determines whether the specified object is valid.
Parameters
validationContextValidationContext – The validation context.
Returns
IEnumerable<ValidationResult> – A collection that holds failed-validation information.
For demonstration purposes, I have created a Model class named Employee and a service for Employee.
Model
public class Employee { public int EmployeeId { get; set; } public string Name { get; set; } public decimal Salary { get; set; } public int Age { get; set; } }
Implementing Service
using System.Threading.Tasks; namespace ArticlesDemo.Service { public interface IEmployeeService { TaskIsEmployeeExist(int employeeId); } }
using System.Threading.Tasks; namespace ArticlesDemo.Service { public class EmployeeService : IEmployeeService { public async TaskIsEmployeeExist(int employeeId) { await Task.Delay(1000); if (employeeId > 1000 && employeeId < 1500) return true; else return false; } } }
Implementing custom validation using IValidatableObject
I want to use different validation for Employee model class that can be validated whenever it was used.
I have four properties in this model, I'm using data annotation for Name property. The other three properties are validated in the ValidateAsync method which is from IValidatableObject Interface.
using ArticlesDemo.Service; using ArticlesDemo.Validation; using Microsoft.Extensions.DependencyInjection; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Threading; using System.Threading.Tasks; namespace ArticlesDemo.Model { public class Employee : AbstractValidatableObject { public int EmployeeId { get; set; } [MinLength(2, ErrorMessage = "Minimum 2 characters required"), MaxLength(50, ErrorMessage = "Maximum 50 characters allowed")] public string Name { get; set; } public decimal Salary { get; set; } public int Age { get; set; } public override async Task<IEnumerable<ValidationResult>> ValidateAsync(ValidationContext validationContext, CancellationToken cancellation) { var errors = new List<ValidationResult>(); if (Salary < 10000) errors.Add(new ValidationResult("Salary cannot be less than $10,000", new[] { nameof(Salary) })); if (Age < 18) errors.Add(new ValidationResult($"Age: {Age} not allowed to work. Minimum age is 18 or more", new[] { nameof(Age) })); //get the required service injected var dbContext = validationContext.GetService<IEmployeeService>(); // Database call through service for validation var isExist = await dbContext.IsEmployeeExist(EmployeeId); if (isExist) { errors.Add(new ValidationResult("EmployeeId exist", new[] { nameof(EmployeeId) })); } return errors; } } }
As you have seen, there was a method in the IEmployeeService to check whether a given employee id exist in the system or not. We can even inject any service and validate the data against database as part of our validation.
Don't forget to add the IEmployeeService in the Startup class.
Startup Class
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddScoped<IEmployeeService, EmployeeService>(); }
Output Validation Message
{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "|87776fa6-4a037412bfcb49c8.", "errors": { "Age": [ "Age: 17 not allowed to work. Minimum age is 18 or more" ], "Salary": [ "Salary cannot be less than $10,000" ], "EmployeeId": [ "EmployeeId exist" ] } }
You may now use this errors property in the UI and show appropriate validation message.

Advantage of using this approach
There is one good advantage for using this approach in ASP NET CORE application. The call lands inside the controller's action method only when the validation is success.
Related Post
- Implement create, read, update, and delete functionalities using Entity Framework Core
- Working with optional body parameters in ASP.NET Core 2.2
Conclusion
In this post, I showed Implementing custom validation using IValidatableObject in ASP.NET Core. That’s all from this post. If you have any questions or just want to chat with me, feel free to leave a comment below.