February 19, 2018 Modern validation patterns in C# Validating user input helps ensure application functionality and prevents stealing sensitive data from your users. Validating user input is one of the most important things in any system. It helps to ensure application functionality but also prevents stealing sensitive data from your users. This is especially true when writing business services because they can be used from multiple applications and third-party companies — when you write public access API. What to Validate? In general, it’s critical to protect the business login. This can be accomplished with various services, managers, providers, stores, etc. In general, everything that is accessible outside of the business layer must have at least a basic validation of input. Furthermore, when you have an application with a client-facing interface, it’s a good practice to validate the user input fields. The way to validate the user input fields varies between applications and used technology and is beyond the scope of this blog post. How Do Teams Choose a Validation Pattern? Depending on the project at hand, there are many approaches to validating data inputs. There is no right way to validate user input, so working with an experienced team is a great way to understand which tools are right for the job at hand. Here are three ways for validating business methods/functions: Exception Throwing Validation Full Model Validation The Validation Rule Pattern Result Class With Validation Extensions Exception Throwing Validation This approach involves a direct system interrupt by using Exception. This pattern, shown below, is the most frequently used validation pattern, and it consists of directly checking inputs and throwing exceptions. void UpdateUserName(int userId, string userName) { if (userId <= 0) { throw new ArgumentException("User identifier must be a positive number"); } if (userName?.Length > 2) { throw new ArgumentException("User name must be more than 2 chars."); } ... } The specifics of this approach are as follows: The executing of the methods ends as soon as the exception is thrown. It is faster to process, but you get only the result of first invalid input. In the example, if the user identifier is 0, you will not know whether the username is valid or not. Any parent code must handle the thrown exceptions or risk unexpected behavior. Having many conditions and validations may make your method big and hard to read or maintain. It is flexible because you can specify any unique rule in any method. It’s possible to have validations that repeat in several places. This approach is very useful when you validate communication between 2 systems. For example, if the ‘UpdateUserName’ method is part of an API, that is used from an external application. Full model validation The idea here is you use a separate class Validator, that validates your entire model. The most famous library that uses this approach is https://fluentvalidation.net/ var validationResult = new MyModelValidator().Validate(); /// validationResult.IsValid is true or false What Do You Need to Know About This Pattern? Pros Good for when you create a public api and want all models to be consistently validated. The code in the method is small and easy to read. Cons It can be hard to use If you have different validation rules in every method. The Validation Rule Pattern The validation rule pattern is derived from the Visitor design pattern. But I have modified it for the purpose of data validation. Here is the way the upper example can be written with this pattern: Private readonly IValidator _validator; Result<bool> UpdateUserAsync(User user) { ValidationResult validation = _validator .AddRule<IdentifierShouldBePositive, int>(it => it.Id) .AddRule(it => it => it.Username, typeof(StringShouldNotBeNullOrWhiteSpace), typeof(StringShouldBeMoreThanFive)) .Validate(user); if (!validation.IsValid) { // Update user return new Result(true); } ... } What Do You Need to Know About This Pattern? Pros All the validation rules are separated and can be maintained separately. The code in the method is small and easy to read. It allows you to apply multiple rules at a time. In the example, we can return both if the user id is valid and the username. Cons Not so flexible as the throwing an exception. For specific messages or conditions, you must create separate classes. Result Class With Validation Extensions This approach relies on the fact that almost all of your services must return some kind of result. Normally this indicates you have a Result class. This is the perfect opportunity to use the C# feature of extensions that adds validation and mapping methods. public Result<User> UpdateUserNameAsync(int userId, string userName) => Result .Validate(userId > 0, ResultStatus.InvalidArgument, "User identifier should be positive number.") .Validate(userName?.Length > 2, ResultStatus.InvalidArgument, "User name must be more than 2 chars.") .Map(valid => repository.GetById(userId)); .Validate(user => user != null, ResultStatus.NotFound, "User was not found!") .Map(user => { user.UserName = userName; return repository.Save(user); }); This pattern is more like a combination of the previous two methods. All conditions are specific for the methods, but we still chain conditions that can be executed simultaneously. What Are the Pros and Cons of Using the Result Class With Validation Extensions Approach? Pros Validation is specific for the current method, which provides flexibility. Can have validations that repeat in several places. Multiple rules can be applied at the same time. It is easy to write and understand. Cons It is harder to debug than the previous methods. Summary There is any universal solution for your project. You have to choose what is best for the projects you are creating and what you want to achieve. It is even possible to use several approaches in different parts of your platform. You can find code examples in this GitHub repository: https://github.com/MentorMate/blogposts/tree/master/ValidationPatterns. Tags Development Share Share on Facebook Share on LinkedIn Share on Twitter Share Share on Facebook Share on LinkedIn Share on Twitter Sign up for our monthly newsletter. Sign up for our monthly newsletter.