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
| Parameter | Description |
|---|---|
value | The value to check |
values | The collection whose elements are checked |
selector | What to check on each element, such as a navigation property |
valueExpr, valuesExpr, selectorExpr | Filled by the compiler with the caller's expressions; not passed by hand |
Behavior
| Method | Throws InvalidOperationException when | Message |
|---|---|---|
AssertLoaded | value is null | <valueExpr> has not been loaded |
AssertEachLoaded | selector 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;
}
}