There are many ways to perform a validation of our models living within the system.
Whether there’s an incoming request from the user who would like to create an account or there’s a need to ensure about the correct amount of money in a bank transaction, the validation process should always (I really mean that) take place. In today’s post, I’d like to present one of the possible solutions that might help you validate your entities.
Let’s start with a definition of the IValidator interface:
1 2 3 4 5 |
public interface IValidator<in T> { void ValidateOrFail(T model); IEnumerable<string> BrokenRules(T model); } |
If the validation fails, there will be a ModelValidationException thrown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class ModelValidationException : Exception { public IEnumerable<string> ValidationErrors { get; } public ModelValidationException() { } public ModelValidationException(string message) : base(message) { } public ModelValidationException(params string[] validationErrors) : this("Validation errors occured.", validationErrors) { } public ModelValidationException(string message, params string[] validationErrors) : base(message) { ValidationErrors = validationErrors; } } |
If you’re not a fan of throwing the exceptions in such cases, you might as well return a boolean or IEnumerable
Now, let’s assume there’s the following class (that would be a ValueObject in the world of DDD):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class UserProfile { public string FirstName { get; protected set; } public string LastName { get; protected set; } public int? Age { get; protected set; } public Gender? Gender { get; protected set; } protected UserProfile(string firstName, string lastName, int? age, Gender? gender) { FirstName = firstName; LastName = lastName; Age = age; Gender = gender; } public static UserProfile Create(string firstName, string lastName, int? age, Gender? gender) => new UserProfile(firstName, lastName, age, gender); } |
And we could define the following profile validator:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class UserProfileValidator : IValidator<UserProfile> { public void ValidateOrFail(UserProfile model) { var brokenRules = BrokenRules(model).ToArray(); if(brokenRules.Any()) throw new ModelValidationException(brokenRules); } public IEnumerable<string> BrokenRules(UserProfile model) { if (model == null) { yield return "Profile can not be null."; yield break; } if (model.Age.HasValue && model.Age < 13) yield return $"Age: {model.Age} is not greater than or equal to 13."; if (model.FirstName?.Length > 100) yield return "First name length can not be longer than 100 characters. " + $"You've typed: {model.FirstName?.Length} characters."; if (model.LastName?.Length > 100) yield return "Last name length can not be longer than 100 characters. " + $"You've typed: {model.FirstName?.Length} characters."; } } } |
Once it’s completed, you can easily use these validators e.g. within your business logic services. It also comes in handy to configure them within the IoC container. For example using it with the Autofac could look like this:
1 2 |
var assembly = Assembly.GetAssembly(typeof(IValidator<>)); builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IValidator<>)); |
And that would be all, it’s a relatively easy concept, yet also quite useful, mostly due to the fact that having a set of interfaces for validation purposes makes it easier for testing the code.
Pingback: Dew Drop - August 8, 2016 (#2304) - Morning Dew
Hi
One suggestion, you can use FluentValidation library
https://github.com/JeremySkinner/FluentValidation
https://fluentvalidation.codeplex.com/wikipage?title=mvc (for ASP projects).
It will improve the code readability.
Hi,
Yes, it’s a very good library, but in case someone would prefer to have his own solution due to some specific reasons it can be achieved using a quite simple interface mentioned above :).
It looks like a nice solution. But where can I use the validation. In userService?
Either in UserService or even separately, for example, let’s say that you would like to have a sophisticated flow of registering a new user account. At first, you could perform the validation, then create an account and eventually send him an email message. All of that business logic may be contain withing different unit such as CommandHandler or so.
Make sense. But I could not imageine to separate validation logic. Message send operation may be in command handler, But how can I seperate the validation?
Look, if you put the login within command handler you can easily define whatever flow comes to your mind. Please check the example below:
https://github.com/noordwind/Collectively.Services.Remarks/blob/master/Collectively.Services.Remarks/Handlers/ResolveRemarkHandler.cs
Hello PIOTR GANKIEWICZ, thank you very much. Can you put or follow one simple code example where your validation is used?