We’ve created the basics of our validation library, but we haven’t yet addressed the problem of aggregation. How do we make it easy for our consumers to combine multiple validation results together into one. Ideally, we want this to be so simple that they don’t have to think about it at all.
Looking back at the start of this series, we can see a good example of what we don’t want to have - a lot of boilerplate code that makes it harder to see what’s actually being checked.
Rewriting this to use AggregateResult would be straightforward:
But this is hardly satisfactory - the level of boilerplate totally dominates the method.
We could add a second constructor to AggregateResult that uses params, allowing us to write:
This isn’t terrible, but it’s not boilerplate free either - the use of AggregateResult is painfully obvious and it draws the readers attention away from the actual function of the method.
What if we could instead write things this way?
To make this work, we need to implement the plus (+) operator. While operator overloading has been possible in C# since before the first release, it’s not a common approach.
By the way, this is why ValidationResult was declared as an abstract class in the first post - operator overloading in C# doesn’t work with interfaces.
On ValidationResult, we declare the operator itself:
After doing some essential parameter checks, the instance methd Add() is invoked to do all the hard work. While I could have called the method Plus(), that didn’t seem like it would flow well - so I used a more idomatic name.
Note that I’ve made a choice here that passing null to add should result in an exception - because I think that no validation result at all is very different to successful validation. That said, you could write the method differently to treat the two as the same - a similar approach to the way some frameworks treat a null string as equal to an empty string.
The declaration of Add is straightforward:
The implementation for SuccessResult is easy: Adding success to anything doesn’t change it:
For an ErrorResult, we need to ensure we always conserve the message, because we don’t want to lose track of any errors that we’ve found. Pay attention to the comments that break down how it works.
Lastly, for an AggregateResult, the logic is pretty similar:
It’s important to observe that we don’t nest AggregateResults - if we’re adding two of them together, we create a new AggregateResult that combines all of the individual results together.