Reference

FunctionalExtensions

Package Trax.CoreNamespace Trax.Core.ExtensionsSource FunctionalExtensions.cs

Extension methods that throw when a value that should have been loaded is null, naming the expression that was checked. They stand in for the null-forgiving operator (!) where a null is a bug worth reporting, such as an EF navigation that was not included in a query.

Signatures

namespace Trax.Core.Extensions;
 
public static class FunctionalExtensions
{
    public static void AssertLoaded<T>(
        [NotNull] this T? value,
        [CallerArgumentExpression("value")] string? valueExpr = null
    );
 
    public static void AssertEachLoaded<T, U>(
        [NotNull] this IEnumerable<T> values,
        Func<T, U> selector,
        [CallerArgumentExpression("values")] string? valuesExpr = null,
        [CallerArgumentExpression("selector")] string? selectorExpr = null
    );
}

Parameters

ParameterDescription
valueThe value to check
valuesThe collection whose elements are checked
selectorWhat to check on each element, such as a navigation property
valueExpr, valuesExpr, selectorExprFilled by the compiler with the caller's expressions; not passed by hand

Behavior

MethodThrows InvalidOperationException whenMessage
AssertLoadedvalue is null<valueExpr> has not been loaded
AssertEachLoadedselector returns null for any element<valuesExpr>(<selectorExpr>) has not been loaded

After AssertLoaded returns, the compiler treats value as not null ([NotNull]).

Example

using Trax.Core.Extensions;
 
public record Customer(string Name);
 
public record Invoice(long Id, Customer? Customer);
 
public static class InvoiceNames
{
    public static IEnumerable<string> CustomerNames(IReadOnlyList<Invoice> invoices)
    {
        invoices.AssertEachLoaded(i => i.Customer);
        return invoices.Select(i => i.Customer!.Name);
    }
 
    public static string CustomerName(Invoice invoice)
    {
        invoice.Customer.AssertLoaded();
        return invoice.Customer.Name;
    }
}

See also