Closed tobias-tengler closed 2 months ago
This is supported since day 1 :)
The base DataLoader fetch looks like the following:
protected override ValueTask FetchAsync(
IReadOnlyList<TKey> keys,
Memory<Result<TValue>> results,
CancellationToken cancellationToken)
Result:
/// <summary>
/// A wrapper for a single value which could contain a valid value or any
/// error.
/// </summary>
/// <typeparam name="TValue">A value type.</typeparam>
public readonly record struct Result<TValue>
{
/// <summary>
/// Creates a new value result.
/// </summary>
/// <param name="value">The value.</param>
public Result(TValue value) : this()
{
Error = default;
Value = value;
Kind = ResultKind.Value;
}
/// <summary>
/// Creates a new error result.
/// </summary>
/// <param name="error">
/// The error.
/// </param>
public Result(Exception error)
{
Error = error ?? throw new ArgumentNullException(nameof(error));
Value = default!;
Kind = ResultKind.Error;
}
/// <summary>
/// Gets a value indicating whether the result is an error, a value or undefined.
/// </summary>
public ResultKind Kind { get; }
/// <summary>
/// Gets the value. If <see cref="Kind"/> is <see cref="ResultKind.Error"/>, returns
/// <c>null</c> or <c>default</c> depending on its type.
/// </summary>
public TValue Value { get; }
/// <summary>
/// Gets an error If <see cref="Kind"/> is <see cref="ResultKind.Error"/>;
/// otherwise <c>null</c>.
/// </summary>
public Exception? Error { get; }
/// <summary>
/// Creates a new error result.
/// </summary>
/// <param name="error">An arbitrary error.</param>
/// <returns>An error result.</returns>
public static Result<TValue> Reject(Exception error) => new(error);
/// <summary>
/// Creates a new value result.
/// </summary>
/// <param name="value">An arbitrary value.</param>
/// <returns>A value result.</returns>
public static Result<TValue> Resolve(TValue value) => new(value);
/// <summary>
/// Creates a new error result or a null result.
/// </summary>
/// <param name="error">An arbitrary error.</param>
public static implicit operator Result<TValue>(Exception? error)
=> error is null ? new Result<TValue>(default(TValue)!) : new Result<TValue>(error);
/// <summary>
/// Creates a new value result.
/// </summary>
/// <param name="value">An arbitrary value.</param>
public static implicit operator Result<TValue>(TValue value)
=> new(value);
/// <summary>
/// Extracts the error from a result.
/// </summary>
/// <param name="result">An arbitrary result.</param>
public static implicit operator Exception?(Result<TValue> result)
=> result.Error;
/// <summary>
/// Extracts the value from a result.
/// </summary>
/// <param name="result">An arbitrary result.</param>
public static implicit operator TValue(Result<TValue> result)
=> result.Value;
}
However, we removed result from the higher level abstractions because most use-cases do not need it ... but you can use it when directly inheriting from DataLoader<TK, TV>
.
Product
Hot Chocolate
Is your feature request related to a problem?
There's a difference between not finding an entity by it's key and being unable to, due to unexpected circumstances. To handle partial errors inside a DataLoader you currently have two options:
Both of these options are not perfect from a DX perspective.
The solution you'd like
It would be great if there were some helpers for dealing with partial DataLoader results. I've seen there already is a
Result
helper withReject
andResolve
methods in Green Donut, but it doesn't seem to be used anywhere. Ideally thedataloader.LoadAsync(id)
could detect whether the returned item is an error or not and re-throw the exception.