Validation strategies

I have a business model with many classes, some logical entities inside this model consist of many different classes (parent-child-grandson). In these various classes, I define constraints that are invariant, for example, that the root of the composite must matter to the code.

Currently, each class implements such an interface ...

public interface IValidatable { IEnumerable<ValidationError> GetErrors(string path); } 

The parent will add a validation error if the code is not installed and then execute GetErrors for each child, which in turn will cause GetErrors for each grandson.

Now I need to check various restrictions for different operations, for example

  • Some constraints should always be checked, since they are invariant
  • Some restrictions should be checked when I want to perform operation X in the root.
  • When performing operation Y, some additional restrictions can be checked.

I thought about adding the Reason parameter to the GetErrors method, but for some reason I cannot say that it is not. I also looked at creating a visitor and a specific implementation for checking for OperationX and another for OperationY, but I donโ€™t like it, because some of the constraint checks will be required for several operations, but not for all of them (for example, OperationX + OperationY requires a date but not OperationZ), and I would not want to duplicate the code that checks.

Any suggestions would be appreciated.

+8
c # design-patterns
source share
8 answers

You have an isolation problem here, since your classes are responsible for their own validation, but the nature of this validation depends on the type of operation you are doing. This means that classes must be aware of the types of operations that can be performed on them, which creates a fairly tight connection between classes and operations that use them.

One of the possible options for creating a parallel set of classes:

 public interface IValidate<T> { IEnumerable<ValidationError> GetErrors(T instance, string path); } public sealed class InvariantEntityValidator : IValidate<Entity> { public IEnumerable<ValidationError> GetErrors(Entity entity, string path) { //Do simple (invariant) validation... } } public sealed class ComplexEntityValidator : IValidate<Entity> { public IEnumerable<ValidationError> GetErrors(Entity entity, string path) { var validator = new InvariantEntityValidator(); foreach (var error in validator.GetErrors(entity, path)) yield return error; //Do additional validation for this complex case } } 

You still need to decide how you want to link the validation classes to the validated different classes. It seems like this should happen at the operations level, since here you know what type of validation should happen. It's hard to say without a better understanding of your architecture.

+3
source share

I would do attribute-based validation:

 public class Entity { [Required, MaxStringLength(50)] public string Property1 { get; set; } [Between(5, 20)] public int Property2 { get; set; } [ValidateAlways, Between(0, 5)] public int SomeOtherProperty { get; set; } [Requires("Property1, Property2")] public void OperationX() { } } 

Each property that is passed to the Requires attribute must be valid for the operation to complete.

Properties that have the ValidateAlways attribute must always be valid - no matter what operation.

In my pseudo-code, Property1 , Property2 and SomeOtherProperty must be valid to execute OperationX .

Of course, you must add a parameter to the Requires attribute to check the validation attributes for the child. But I canโ€™t suggest how to do this without seeing some sample code.

Maybe something like this:

 [Requires("Property1, Property2, Child2: Property3")] 

If necessary, you can also use strongly typed property pointers with lambda expressions instead of strings ( Example ).

+3
source share

I would suggest using the Fluent Validation For library . Net . This library allows you to easily and flexibly configure validators, and if you need different checks for different operations, you can very easily use the one that is used for this specific operation (and change them).

+2
source share

I used the Sptring.NET validation mechanism for the same reason - it allows the use of conditional validators . You simply define the rules - which check to apply and under what conditions the rest do the same. Good thing your business logic is no longer contaminated with validation interfaces

You can find more information in the documentation at springframework.net . I just copy the sample for my document to show what it looks like:

<v:condition test="StartingFrom.Date >= DateTime.Today" when="StartingFrom.Date != DateTime.MinValue">

<v:message id="error.departureDate.inThePast" providers="departureDateErrors, validationSummary"/>

</v:condition>

In this example, the StartFrom property of the Trip object is compared to see if it will be later than the current date, i.e. DateTime, but only when the date was set (the initial value of StartFrom.Date was set to DateTime.MinValue).

The state validator can be considered the "mother of all validators." You can use it to achieve almost everything that can be achieved using other types of validators, but in some cases the test expression can be very complex, so you can use a more specific type of validator if possible. However, the state validator is still the best choice if you need to check whether a particular value belongs to a certain range, or perform a similar test, since these conditions are pretty easy to write.

+1
source share

If you use .Net 4.0, you can use Code Contracts to control this.

+1
source share

Try to read this article from top to bottom, I got a lot of ideas from this.

http://codebetter.com/jeremymiller/2007/06/13/build-your-own-cab-part-9-domain-centric-validation-with-the-notification-pattern/

This is attribute-based authentication with a notification that wraps these checks to higher levels.

0
source share

I would separate the logic of checking the option, perhaps with the visitor, as you mentioned. By separating validation from classes, you will keep your operations separate from your data, and this can really help keep it clean.

Think about it the same way - if you take part in your verification and work with your data classes, think about what it will look like a year later, when you make a promotion, and you need to add a new operation. If each work verification rule and work logic are separate, it is pretty much just an โ€œaddโ€ - you create a new operation and a new validation visitor to go with it. On the other hand, if you need to go back and touch the logic of โ€œif operation == xโ€ in each of your data classes, then you have an additional risk for regression errors, etc.

0
source share

I rely on a proven validation strategy that is implemented in the .net framework in the System.ComponentModel.DataAnnotations ' namespace ' and, for example, is used in ASP.NET MVC.

This method provides an unobtrusive way to apply validation rules (using attributes or implementing IValidatableObject) and the ability to validate rules.

Scott Allen described this path in a large article, Manual Validation with Data Annotations .

  • if you want to apply validation attributes on an interface, see MetadataTypeAttribute
  • for a better understanding of how this works, see the MS source code
0
source share

All Articles