Skip to content

Validating instances

Call Validate directly, inspect the result, or throw with ThrowIfInvalid.

2 min read

You do not need Immediate.Handlers to use Immediate.Validations. Every [Validate] type gets a public static Validate method you can call from anywhere.

public void Method(Query query)
{
	var result = Query.Validate(query);

	if (!result.IsValid)
	{
		foreach (var error in result.Errors)
			Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
	}
}

ValidationResult exposes IsValid, an IReadOnlyList<ValidationError> of Errors, and implements IEnumerable<ValidationError> — so you can foreach it or LINQ over it directly.

Validating through an interface

The generated type also implements the non-generic IValidationTarget, whose Validate() is an explicit implementation. Use it when you have an instance but not its concrete type:

public void Method(IValidationTarget target)
{
	var result = target.Validate();
}

Both Validate shapes have an overload taking an existing ValidationResult, which accumulates into it and returns it — useful when validating several objects into one result:

var errors = new ValidationResult();

_ = Query.Validate(query, errors);
_ = Command.Validate(command, errors);

if (!errors.IsValid) { /* … */ }

Throwing on failure

ValidationException.ThrowIfInvalid throws a ValidationException when validation fails and does nothing when it passes. There are five overloads.

// validate and throw, default title "Validation failed"
ValidationException.ThrowIfInvalid(query);

// validate and throw with a custom title
ValidationException.ThrowIfInvalid(query, "Query is invalid");

// validate and throw with a title computed from the instance
ValidationException.ThrowIfInvalid(query, static q => $"Query {q.Id} is invalid");

// throw from a result you already have
var result = Query.Validate(query);
ValidationException.ThrowIfInvalid(result);

// …with a custom title
ValidationException.ThrowIfInvalid(result, "Query is invalid");

The three instance overloads are constrained to IValidationTarget<T>, so they only compile for a type that declares the interface. The two ValidationResult overloads work with any result, including one you assembled by hand.

ValidationException carries the failures on Errors and the title on Title. Its Message is the title followed by one line per failure:

Query is invalid:
 -- Id: 'Id' must be greater than '0'.
 -- Name: 'Name' must not be empty.

In a handler pipeline

Doing this by hand in every handler is exactly what ValidationBehavior<,> removes. See Integrating with Immediate.Handlers.