Show / Hide Table of Contents

Returning results from rules

Executing the GetResultAsync method(s) of a rule class should most commonly lead to one of two outcomes:

  • The method returns a pass result
  • The method returns a failure result

In truly exceptional scenarios it is acceptable to either throw an exception from the rule class or to return an error result.

Returning a pass or failure result

In order to return pass/failure results it is strongly recommended to use the static class CommonResults. This class is intended to be used via the using static syntax, as demonstrated below.

using static CSF.Validation.Rules.CommonResults;

public class NotAString : IRule<object>
{
    public ValueTask<RuleResult> GetResultAsync(object validated,
                                                RuleContext context,
                                                CancellationToken token = default)
    {
        // The FailAsync and PassAsync methods are defined on CommonResults.
        return validated is string ? FailAsync() : PassAsync();
    }
}

As well as the pass & fail scenarios here, there are also methods dealing with error results; we will cover these below.

Synchronous or asynchronous operation

The example above is of a rule that runs synchronously; there is nothing to await and the GetResultAsync method does not need to be made async. All overloads of the PassAsync, FailAsync & ErrorAsync methods upon the CommonResults class will return completed tasks with a result already available.

Rules may also operate asynchronously. This is useful where the rule logic requires use of an awaitable method from a dependency, such as reading from a database. For these results you may use overloads of the Pass, Fail & Error methods from CommonResults. These methods are more suitable when you wish to use the async keyword with the GetResultAsync method.

Returning additional data

All of the methods defined on the CommonResults class present an overload that accepts a dictionary of result data. This allows advanced usages scenarios in which supplemental information may be returned along with the overall pass, fail or error outcome.

Throwing exceptions from rule classes

Non-trivial rules can raise unexpected exceptions, it is a simple fact of software development. For example a rule which must read a value from a database is likely to throw an exception if the database is unavailable.

CSF.Validation attempts to minimuse the amount of boilerplate try/catch that developers must write in their rule classes. Any and all exceptions which originate in a GetResultAsync method are caught automatically by the framework. These exceptions are translated to validation results which have an outcome of RuleOutcome.Errored. The ValidationRuleResult resulting from this caught exception will include a reference to the exception which was thrown by the rule.

How the validator treats errors

The validator treats error results similarly to failures; an error result does not count as a pass. For rules which have dependencies, if any of a rule's dependencies' results is an error then the dependent rule will not be executed.

When one or more validation results has the outcome RuleOutcome.Errored, the framework's default behaviour is to throw an exception at the end of validation. That exception will contain the complete validation result including all of the error results. This behaviour may be overridden by specifying the RuleThrowingBehaviour property on an instance of ValidationOptions passed to the validator.

Returning error results manually

It is also possible, using the CommonResults class to manually create and return an error result, via either of the ErrorAsync() or Error() methods. The only practical difference between using these methods and allowing the rule GetResultAsync method to throw an exception is:

  • A manually-returned error result does not require an exception
  • A manually-returned error result may contain a dictionary of result data
  • Improve this Doc
In This Article
Back to top Generated by DocFX