Getting Started with Pragmatic.Ensure
This guide will get you up and running with Pragmatic.Ensure in minutes.
Installation
Section titled “Installation”# Core package (ThrowIf and Is patterns)dotnet add package Pragmatic.Ensure
# Optional: Result integration (Check pattern for domain validation)dotnet add package Pragmatic.Ensure.ResultQuick Start
Section titled “Quick Start”ThrowIf Pattern (Constructor Guards)
Section titled “ThrowIf Pattern (Constructor Guards)”The most common use case is validating constructor parameters:
using Pragmatic.Ensure;
public class Order{ public Guid Id { get; } public string CustomerEmail { get; } public decimal Amount { get; } public IReadOnlyList<OrderItem> Items { get; }
public Order(Guid id, string customerEmail, decimal amount, IReadOnlyList<OrderItem> items) { // Guards automatically capture parameter names via CallerArgumentExpression Ensure.ThrowIfEmpty(id); // ArgumentException if Guid.Empty Ensure.ThrowIfNotEmail(customerEmail); // ArgumentException if invalid email Ensure.ThrowIfNegativeOrZero(amount); // ArgumentOutOfRangeException if <= 0 Ensure.ThrowIfNullOrEmpty(items); // ArgumentNullException or ArgumentException
Id = id; CustomerEmail = customerEmail; Amount = amount; Items = items; }}Is Pattern (Conditional Logic)
Section titled “Is Pattern (Conditional Logic)”For validation without exceptions:
using Pragmatic.Ensure;
public void ProcessUser(string? name, string? email){ // Null-state analysis works correctly if (Ensure.IsNotNullOrWhiteSpace(name)) { // 'name' is known to be non-null here Console.WriteLine($"Processing: {name}"); }
// Combine checks if (Ensure.IsEmail(email) && Ensure.IsLengthInRange(email, 5, 254)) { SendEmail(email); }}Three Patterns, Three Purposes
Section titled “Three Patterns, Three Purposes”| Pattern | Package | Purpose | Example |
|---|---|---|---|
ThrowIf* | Pragmatic.Ensure | Programming errors (bugs) | Constructor guards, method preconditions |
Is* | Pragmatic.Ensure | Boolean checks | Conditional flows, quick validation |
Check.* | Pragmatic.Ensure.Result | Domain validation | User input, business rules with typed errors |
When to Use ThrowIf
Section titled “When to Use ThrowIf”- Constructor parameter validation
- Public API method preconditions
- Invariant checking
- Any situation where invalid data indicates a bug
public void Transfer(Account from, Account to, decimal amount){ Ensure.ThrowIfNull(from); Ensure.ThrowIfNull(to); Ensure.ThrowIfNegativeOrZero(amount); Ensure.ThrowIfFalse(from.Balance >= amount, "Insufficient funds");
// ... proceed with transfer}When to Use Is
Section titled “When to Use Is”- Conditional branching
- User input filtering
- Optional processing
- Any situation where invalid data is expected and handled
public void ImportContacts(IEnumerable<ContactDto> contacts){ foreach (var contact in contacts) { if (!Ensure.IsEmail(contact.Email)) { _logger.LogWarning("Skipping invalid email: {Email}", contact.Email); continue; }
ProcessContact(contact); }}When to Use Check (Result Pattern)
Section titled “When to Use Check (Result Pattern)”- Domain validation where failure is expected (user input, business rules)
- When you need typed errors with rich information
- Composable validation chains with
Bind() - Integration with the Result pattern
using Pragmatic.Ensure.Result;
public VoidResult<ValidationError> ValidateUser(UserDto dto){ return Check.NotNullOrWhiteSpace(dto.Name, new ValidationError("Name required")) .Bind(_ => Check.Email(dto.Email, new ValidationError("Invalid email"))) .Bind(_ => Check.InRange(dto.Age, 0, 150, new ValidationError("Invalid age")));}
// Chain validations with typed errorsvar result = ValidateUser(userDto);if (result.IsFailure){ return BadRequest(result.Error.Message);}Automatic Parameter Names
Section titled “Automatic Parameter Names”Thanks to [CallerArgumentExpression], you never need to specify parameter names:
public void Process(string customerName, int orderId){ Ensure.ThrowIfNullOrWhiteSpace(customerName); // Throws: ArgumentException: Value cannot be null or whitespace. (Parameter 'customerName')
Ensure.ThrowIfNegative(orderId); // Throws: ArgumentOutOfRangeException: Value cannot be negative. (Parameter 'orderId')}Common Validation Scenarios
Section titled “Common Validation Scenarios”Entity Validation
Section titled “Entity Validation”public class User{ public User(Guid id, string name, string email, int age) { Ensure.ThrowIfEmpty(id); Ensure.ThrowIfNullOrWhiteSpace(name); Ensure.ThrowIfNotEmail(email); Ensure.ThrowIfOutOfRange(age, 0, 150);
// ... assign properties }}Service Method Guards
Section titled “Service Method Guards”public async Task<Order> CreateOrder(CreateOrderRequest request){ Ensure.ThrowIfNull(request); Ensure.ThrowIfNullOrEmpty(request.Items); Ensure.ThrowIfNegativeOrZero(request.TotalAmount);
// ... create order}Collection Operations
Section titled “Collection Operations”public void ProcessBatch(IEnumerable<Item> items){ Ensure.ThrowIfNullOrEmpty(items); // Checks null and empty
foreach (var item in items) { // Safe to enumerate }}Next Steps
Section titled “Next Steps”- API Reference - Complete method documentation
- Best Practices - Advanced patterns and tips