Migration Guides
v10.1.0 → v10.2.0
This release is additive. It adds MustBeValidIban() and updates the RA.Utilities.Core.ValueObjects dependency to 10.2.0, which introduces the IBAN value object. No existing rule, signature, or behavior changed.
What's new
| Type / rule | Change |
|---|---|
RA.Utilities.Application.Validation.Extensions.IbanValidatorExtensions | New |
MustBeValidIban() | New FluentValidation rule for string? IBAN properties |
RA.Utilities.Core.ValueObjects dependency | Updated to 10.2.0 (adds IBAN) |
FluentValidation dependency | Unchanged (12.1.1) |
Why this change?
IBAN is a new value object in RA.Utilities.Core.ValueObjects 10.2.0. The package's other value-object rules already validate through the domain types, so request validation and the domain stay aligned only if an IBAN rule exists too.
MustBeValidIban() follows the same design as MustBeValidCurrency(), MustBeValidEmail(), and MustBeValidSsn():
- It constructs
new IBAN(value)behind the scenes and fails the rule when the constructor throws. - It normalizes casing and grouping separators, and enforces the 13–34 character structure plus the ISO 7064 mod-97 checksum.
- It fails on
null, empty, and whitespace input, so the rule doubles as a required check. - It reports the generic
BadRequestExceptionmessage ("The request is invalid."); useWithMessage(...)for a property-specific message.
Migration steps
1. Upgrade the package
dotnet add package RA.Utilities.Application.Validation --version 10.2.0
2. Add the rule where IBANs are accepted
using RA.Utilities.Application.Validation.Extensions;
public sealed class CreatePaymentValidator : AbstractValidator<CreatePaymentRequest>
{
public CreatePaymentValidator()
{
RuleFor(x => x.Iban).MustBeValidIban();
}
}
3. Decide whether the IBAN is required
The rule fails on missing values. If the field is optional, apply a condition:
RuleFor(x => x.Iban)
.MustBeValidIban()
.When(x => !string.IsNullOrWhiteSpace(x.Iban));
4. Keep country-length checks separate
The rule validates structure and the checksum, not the length required by each country's registry. If your domain needs that, validate it separately and report BaseErrorCode.IbanLength — see IbanValidatorExtensions.
Breaking changes
None. Existing rules and ValidationUtilities signatures are unchanged.
v10.0.1 → v10.1.0
Breaking Change: ValidateAsync now requires a CancellationToken
ValidationUtilities.ValidateAsync has a new third parameter — a CancellationToken — and it has no default, so existing two-argument calls no longer compile (CS7036). Pass CancellationToken.None if you have no token to propagate, or forward the token from your request pipeline.
Before (10.0.1):
ValidationFailure[] failures = await ValidationUtilities.ValidateAsync(request, validators);
After (10.1.0):
ValidationFailure[] failures = await ValidationUtilities.ValidateAsync(request, validators, cancellationToken);
The token is forwarded to every validator, so validators with async rules honour cancellation. The ValidationBehavior in RA.Utilities.Feature already forwards the request token.
Other Changes (non-breaking)
- New rules:
MustBeValidCurrency(),MustBeValidEmail()andMustBeValidSsn()validate through theCurrency,EmailandSSNvalue objects ofRA.Utilities.Core.ValueObjects— any input the value object rejects fails the rule. Additive only. - New dependency:
RA.Utilities.Core.ValueObjects(10.1.0) — pulled in transitively, no action needed. - Extension classes refactored to C# 14 extension members:
CurrencyValidatorExtensions,EmailValidatorExtensionsandSsnValidatorExtensionsare now implemented as C# 14 extension members. Method names, parameters, and call-site syntax are unchanged.
v10.0.0 → v10.0.1
Breaking Change: ValidationErrors → ValidationError
CreateValidationErrorResult now maps failures to the renamed ValidationError type from RA.Utilities.Core.Exceptions 10.0.4 (the class represents a single error, hence the singular name). The BadRequestException.Errors array is now ValidationError[].
If you only consume exception.Errors and never name the old type, you don't need to change anything. Code that references ValidationErrors by name must be updated.
Why this change?
- The rename aligns with
RA.Utilities.Core.Exceptions10.0.4, whereValidationErrorswas renamed toValidationErrorand its constructor now requires the error message. - The old collection-initializer pattern could produce errors without a message; the new constructor guarantees one.
What changed
| Before (10.0.0) | After (10.0.1) | |
|---|---|---|
| Element type | ValidationErrors | ValidationError |
| Constructor | parameterless + object initializer | ValidationError(string errorMessage) |
| Message | optional | required |
Migration Steps
1. Rename references to the error type
Before:
ValidationErrors error = exception.Errors[0];
var error = new ValidationErrors { ErrorMessage = "Something is wrong." };
After:
ValidationError error = exception.Errors[0];
var error = new ValidationError("Something is wrong.");
2. Update the RA.Utilities.Core.Exceptions dependency
The new payload type lives in RA.Utilities.Core.Exceptions 10.0.4+. Upgrading this package pulls it in transitively; if you reference Core.Exceptions explicitly, update it too. See the Core.Exceptions migration guide for the full rename details.
Behavior Change: null currency values now pass
In 10.0.0, MustMatchesCurrencyFormat() rejected null values with the format error message (FluentValidation 12 runs Must on null). In 10.0.1, null passes the format rule — requiredness is the job of NotNull() / NotEmpty().
If you relied on the old behavior to reject null currencies, make the requirement explicit:
Before (10.0.0):
RuleFor(x => x.Currency).MustMatchesCurrencyFormat(); // null → error
After (10.0.1):
RuleFor(x => x.Currency).NotNull().MustMatchesCurrencyFormat(); // null → error
Bug Fix: duplicate failures from parallel validators
10.0.0 shared a single ValidationContext across all validators in ValidateAsync, so each validator's result contained every validator's failures — one failing rule could appear once per registered validator (with a data race on top when validators were genuinely async). 10.0.1 creates a fresh context per validator. No migration is required; if you worked around the duplicates by de-duplicating failures, that workaround can be removed.
Full Changelog
See the package release notes for a complete version history.