FailIf accumulates; Bind short-circuits
Now the idiom you’ll write most. To validate, you chain guards with FailIf, then terminate with OrSuccess. The crucial, easy-to-miss detail: FailIf accumulates. It does not stop at the first failure. If three rules are broken, the Result comes back with all three errors, so the user fixes everything at once instead of playing whack-a-mole.
public static Result<FlightNoteVo> Create(string? ejdpUniqueFlightKey, string? body)
{
var trimmedBody = (body ?? string.Empty).Trim();
return Result<FlightNoteVo>
.FailIf(string.IsNullOrWhiteSpace(ejdpUniqueFlightKey), MissingFlightKey)
.FailIf(trimmedBody.Length > MaxBodyLength,
new EjError(nameof(Body), $"A note body must be at most {MaxBodyLength} characters."))
.OrSuccess(() => new FlightNoteVo(ejdpUniqueFlightKey!.Trim(), trimmedBody));
}OrSuccess takes either a value or a factory Func<T>. Use the factory form (() => new ...) when building the value would be wrong or would throw if the guards had failed, since the factory only runs on the success path. Notice ejdpUniqueFlightKey! inside it: the null-forgiving ! is safe precisely because the FailIf above already proved it’s non-null on this path.
Sometimes you need the opposite: step B depends on step A’s value, so if A fails there’s nothing to feed B. That’s Bind. It short-circuits: on failure it returns the failure untouched; on success it runs the next step with the value. Use FailIf to gather independent rules, Bind to sequence dependent steps.
DisruptionDelayVo.Create(isCancelled, delayMinutes)
.Bind(delay => Eu261Vo.Create(isExtraordinary, delay, pax, routeClaimRate, weighting));Try it yourself
Write a Create with an accumulating chain
Build the muscle: turn two rules into an accumulating factory. Model it on FlightNoteVo.Create above.
- Imagine a
CrewCountVowrapping an int that must be > 0 and <= 20 - Write
public static Result<CrewCountVo> Create(int value)usingResult<CrewCountVo>.FailIf(value <= 0, err1).FailIf(value > 20, err2).OrSuccess(() => new CrewCountVo(value)) - Pass
value = -5: convince yourself the returned Result carries the> 0error - Pass
value = 25: it carries the<= 20error. Neither call throws
FailIf or Bind
You validate two independent fields on a request, and you want the caller to see BOTH errors if both are wrong. Which do you reach for?
Show answer
Correct answer: A — FailIf, because it accumulates all errors instead of stopping at the first
FailIf accumulates: chain both guards and the Result comes back with both errors. Bind short-circuits on the first failure, which is right for DEPENDENT steps but wrong when you want to report every independent problem at once.
The two chaining shapes
Lock the distinction in.
In one line each: when do you use FailIf and when do you use Bind?
Reveal answer
FailIf for independent validation rules you want to accumulate (report all failures at once). Bind for a dependent step whose input is the previous step's success value, so it short-circuits if the previous step failed.