Git Product home page Git Product logo

error-or's Introduction

drawing

NuGet

Build publish ErrorOr to nuget

GitHub contributors GitHub Stars GitHub license codecov

A simple, fluent discriminated union of an error or a result.

dotnet add package ErrorOr

Give it a star โญ!

Loving it? Show your support by giving this project a star!

Getting Started ๐Ÿƒ

Replace throwing exceptions with ErrorOr<T>

This ๐Ÿ‘‡

public float Divide(int a, int b)
{
    if (b == 0)
    {
        throw new Exception("Cannot divide by zero");
    }

    return a / b;
}

try
{
    var result = Divide(4, 2);
    Console.WriteLine(result * 2); // 4
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
    return;
}

Turns into this ๐Ÿ‘‡

public ErrorOr<float> Divide(int a, int b)
{
    if (b == 0)
    {
        return Error.Unexpected(description: "Cannot divide by zero");
    }

    return a / b;
}

var result = Divide(4, 2);

if (result.IsError)
{
    Console.WriteLine(result.FirstError.Description);
    return;
}

Console.WriteLine(result.Value * 2); // 4

Or, using Then/Else and Switch/Match, you can do this ๐Ÿ‘‡

Divide(4, 2)
    .Then(val => val * 2)
    .SwitchFirst(
        onValue: Console.WriteLine, // 4
        onFirstError: error => Console.WriteLine(error.Description));

Support For Multiple Errors

Internally, the ErrorOr object has a list of Errors, so if you have multiple errors, you don't need to compromise and have only the first one.

public class User(string _name)
{
    public static ErrorOr<User> Create(string name)
    {
        List<Error> errors = [];

        if (name.Length < 2)
        {
            errors.Add(Error.Validation(description: "Name is too short"));
        }

        if (name.Length > 100)
        {
            errors.Add(Error.Validation(description: "Name is too long"));
        }

        if (string.IsNullOrWhiteSpace(name))
        {
            errors.Add(Error.Validation(description: "Name cannot be empty or whitespace only"));
        }

        if (errors.Count > 0)
        {
            return errors;
        }

        return new User(name);
    }
}

Various Functional Methods and Extension Methods

The ErrorOr object has a variety of methods that allow you to work with it in a functional way.

This allows you to chain methods together, and handle the result in a clean and concise way.

Real world example

return await _userRepository.GetByIdAsync(id)
    .Then(user => user.IncrementAge()
        .Then(success => user)
        .Else(errors => Error.Unexpected("Not expected to fail")))
    .FailIf(user => !user.IsOverAge(18), UserErrors.UnderAge)
    .ThenDo(user => _logger.LogInformation($"User {user.Id} incremented age to {user.Age}"))
    .ThenAsync(user => _userRepository.UpdateAsync(user))
    .Match(
        _ => NoContent(),
        errors => errors.ToActionResult());

Simple Example with intermediate steps

No Failure

ErrorOr<string> foo = await "2".ToErrorOr()
    .Then(int.Parse) // 2
    .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big") // 2
    .ThenDoAsync(Task.Delay) // Sleep for 2 milliseconds
    .ThenDo(val => Console.WriteLine($"Finished waiting {val} milliseconds.")) // Finished waiting 2 milliseconds.
    .ThenAsync(val => Task.FromResult(val * 2)) // 4
    .Then(val => $"The result is {val}") // "The result is 4"
    .Else(errors => Error.Unexpected(description: "Yikes")) // "The result is 4"
    .MatchFirst(
        value => value, // "The result is 4"
        firstError => $"An error occurred: {firstError.Description}");

Failure

ErrorOr<string> foo = await "5".ToErrorOr()
    .Then(int.Parse) // 5
    .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big") // Error.Validation()
    .ThenDoAsync(Task.Delay) // Error.Validation()
    .ThenDo(val => Console.WriteLine($"Finished waiting {val} milliseconds.")) // Error.Validation()
    .ThenAsync(val => Task.FromResult(val * 2)) // Error.Validation()
    .Then(val => $"The result is {val}") // Error.Validation()
    .Else(errors => Error.Unexpected(description: "Yikes")) // Error.Unexpected()
    .MatchFirst(
        value => value,
        firstError => $"An error occurred: {firstError.Description}"); // An error occurred: Yikes

Creating an ErrorOr instance

Using implicit conversion

There are implicit converters from TResult, Error, List<Error> to ErrorOr<TResult>

ErrorOr<int> result = 5;
ErrorOr<int> result = Error.Unexpected();
ErrorOr<int> result = [Error.Validation(), Error.Validation()];
public ErrorOr<int> IntToErrorOr()
{
    return 5;
}
public ErrorOr<int> SingleErrorToErrorOr()
{
    return Error.Unexpected();
}
public ErrorOr<int> MultipleErrorsToErrorOr()
{
    return [
        Error.Validation(description: "Invalid Name"),
        Error.Validation(description: "Invalid Last Name")
    ];
}

Using The ErrorOrFactory

ErrorOr<int> result = ErrorOrFactory.From(5);
ErrorOr<int> result = ErrorOrFactory.From<int>(Error.Unexpected());
ErrorOr<int> result = ErrorOrFactory.From<int>([Error.Validation(), Error.Validation()]);
public ErrorOr<int> GetValue()
{
    return ErrorOrFactory.From(5);
}
public ErrorOr<int> SingleErrorToErrorOr()
{
    return ErrorOrFactory.From<int>(Error.Unexpected());
}
public ErrorOr<int> MultipleErrorsToErrorOr()
{
    return ErrorOrFactory.From([
        Error.Validation(description: "Invalid Name"),
        Error.Validation(description: "Invalid Last Name")
    ]);
}

Using The ToErrorOr Extension Method

ErrorOr<int> result = 5.ToErrorOr();
ErrorOr<int> result = Error.Unexpected().ToErrorOr<int>();
ErrorOr<int> result = new[] { Error.Validation(), Error.Validation() }.ToErrorOr<int>();

Properties

IsError

ErrorOr<int> result = User.Create();

if (result.IsError)
{
    // the result contains one or more errors
}

Value

ErrorOr<int> result = User.Create();

if (!result.IsError) // the result contains a value
{
    Console.WriteLine(result.Value);
}

Errors

ErrorOr<int> result = User.Create();

if (result.IsError)
{
    result.Errors // contains the list of errors that occurred
        .ForEach(error => Console.WriteLine(error.Description));
}

FirstError

ErrorOr<int> result = User.Create();

if (result.IsError)
{
    var firstError = result.FirstError; // only the first error that occurred
    Console.WriteLine(firstError == result.Errors[0]); // true
}

ErrorsOrEmptyList

ErrorOr<int> result = User.Create();

if (result.IsError)
{
    result.ErrorsOrEmptyList // List<Error> { /* one or more errors */  }
    return;
}

result.ErrorsOrEmptyList // List<Error> { }

Methods

Match

The Match method receives two functions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Match

string foo = result.Match(
    value => value,
    errors => $"{errors.Count} errors occurred.");

MatchAsync

string foo = await result.MatchAsync(
    value => Task.FromResult(value),
    errors => Task.FromResult($"{errors.Count} errors occurred."));

MatchFirst

The MatchFirst method receives two functions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Unlike Match, if the state is error, MatchFirst's onError function receives only the first error that occurred, not the entire list of errors.

string foo = result.MatchFirst(
    value => value,
    firstError => firstError.Description);

MatchFirstAsync

string foo = await result.MatchFirstAsync(
    value => Task.FromResult(value),
    firstError => Task.FromResult(firstError.Description));

Switch

The Switch method receives two actions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Switch

result.Switch(
    value => Console.WriteLine(value),
    errors => Console.WriteLine($"{errors.Count} errors occurred."));

SwitchAsync

await result.SwitchAsync(
    value => { Console.WriteLine(value); return Task.CompletedTask; },
    errors => { Console.WriteLine($"{errors.Count} errors occurred."); return Task.CompletedTask; });

SwitchFirst

The SwitchFirst method receives two actions, onValue and onError, onValue will be invoked if the result is success, and onError is invoked if the result is an error.

Unlike Switch, if the state is error, SwitchFirst's onError function receives only the first error that occurred, not the entire list of errors.

result.SwitchFirst(
    value => Console.WriteLine(value),
    firstError => Console.WriteLine(firstError.Description));

SwitchFirstAsync

await result.SwitchFirstAsync(
    value => { Console.WriteLine(value); return Task.CompletedTask; },
    firstError => { Console.WriteLine(firstError.Description); return Task.CompletedTask; });

Then

Then

Then receives a function, and invokes it only if the result is not an error.

ErrorOr<int> foo = result
    .Then(val => val * 2);

Multiple Then methods can be chained together.

ErrorOr<string> foo = result
    .Then(val => val * 2)
    .Then(val => $"The result is {val}");

If any of the methods return an error, the chain will break and the errors will be returned.

ErrorOr<int> Foo() => Error.Unexpected();

ErrorOr<string> foo = result
    .Then(val => val * 2)
    .Then(_ => GetAnError())
    .Then(val => $"The result is {val}") // this function will not be invoked
    .Then(val => $"The result is {val}"); // this function will not be invoked

ThenAsync

ThenAsync receives an asynchronous function, and invokes it only if the result is not an error.

ErrorOr<string> foo = await result
    .ThenAsync(val => DoSomethingAsync(val))
    .ThenAsync(val => DoSomethingElseAsync($"The result is {val}"));

ThenDo and ThenDoAsync

ThenDo and ThenDoAsync are similar to Then and ThenAsync, but instead of invoking a function that returns a value, they invoke an action.

ErrorOr<string> foo = result
    .ThenDo(val => Console.WriteLine(val))
    .ThenDo(val => Console.WriteLine($"The result is {val}"));
ErrorOr<string> foo = await result
    .ThenDoAsync(val => Task.Delay(val))
    .ThenDo(val => Console.WriteLine($"Finsihed waiting {val} seconds."))
    .ThenDoAsync(val => Task.FromResult(val * 2))
    .ThenDo(val => $"The result is {val}");

Mixing Then, ThenDo, ThenAsync, ThenDoAsync

You can mix and match Then, ThenDo, ThenAsync, ThenDoAsync methods.

ErrorOr<string> foo = await result
    .ThenDoAsync(val => Task.Delay(val))
    .Then(val => val * 2)
    .ThenAsync(val => DoSomethingAsync(val))
    .ThenDo(val => Console.WriteLine($"Finsihed waiting {val} seconds."))
    .ThenAsync(val => Task.FromResult(val * 2))
    .Then(val => $"The result is {val}");

FailIf

FailIf receives a predicate and an error. If the predicate is true, FailIf will return the error. Otherwise, it will return the value of the result.

ErrorOr<int> foo = result
    .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big"));

Once an error is returned, the chain will break and the error will be returned.

var result = "2".ToErrorOr()
    .Then(int.Parse) // 2
    .FailIf(val => val > 1, Error.Validation(description: $"{val} is too big") // validation error
    .Then(num => num * 2) // this function will not be invoked
    .Then(num => num * 2) // this function will not be invoked

Else

Else receives a value or a function. If the result is an error, Else will return the value or invoke the function. Otherwise, it will return the value of the result.

Else

ErrorOr<string> foo = result
    .Else("fallback value");
ErrorOr<string> foo = result
    .Else(errors => $"{errors.Count} errors occurred.");

ElseAsync

ErrorOr<string> foo = await result
    .ElseAsync(Task.FromResult("fallback value"));
ErrorOr<string> foo = await result
    .ElseAsync(errors => Task.FromResult($"{errors.Count} errors occurred."));

Mixing Features (Then, FailIf, Else, Switch, Match)

You can mix Then, FailIf, Else, Switch and Match methods together.

ErrorOr<string> foo = await result
    .ThenDoAsync(val => Task.Delay(val))
    .FailIf(val => val > 2, Error.Validation(description: $"{val} is too big"))
    .ThenDo(val => Console.WriteLine($"Finished waiting {val} seconds."))
    .ThenAsync(val => Task.FromResult(val * 2))
    .Then(val => $"The result is {val}")
    .Else(errors => Error.Unexpected())
    .MatchFirst(
        value => value,
        firstError => $"An error occurred: {firstError.Description}");

Error Types

Each Error instance has a Type property, which is an enum value that represents the type of the error.

Built in error types

The following error types are built in:

public enum ErrorType
{
    Failure,
    Unexpected,
    Validation,
    Conflict,
    NotFound,
    Unauthorized,
    Forbidden,
}

Each error type has a static method that creates an error of that type. For example:

var error = Error.NotFound();

optionally, you can pass a code, description and metadata to the error:

var error = Error.Unexpected(
    code: "User.ShouldNeverHappen",
    description: "A user error that should never happen",
    metadata: new Dictionary<string, object>
    {
        { "user", user },
    });

The ErrorType enum is a good way to categorize errors.

Custom error types

You can create your own error types if you would like to categorize your errors differently.

A custom error type can be created with the Custom static method

public static class MyErrorTypes
{
    const int ShouldNeverHappen = 12;
}

var error = Error.Custom(
    type: MyErrorTypes.ShouldNeverHappen,
    code: "User.ShouldNeverHappen",
    description: "A user error that should never happen");

You can use the Error.NumericType method to retrieve the numeric type of the error.

var errorMessage = Error.NumericType switch
{
    MyErrorType.ShouldNeverHappen => "Consider replacing dev team",
    _ => "An unknown error occurred.",
};

Built in result types (Result.Success, ..)

There are a few built in result types:

ErrorOr<Success> result = Result.Success;
ErrorOr<Created> result = Result.Created;
ErrorOr<Updated> result = Result.Updated;
ErrorOr<Deleted> result = Result.Deleted;

Which can be used as following

ErrorOr<Deleted> DeleteUser(Guid id)
{
    var user = await _userRepository.GetByIdAsync(id);
    if (user is null)
    {
        return Error.NotFound(description: "User not found.");
    }

    await _userRepository.DeleteAsync(user);
    return Result.Deleted;
}

Organizing Errors

A nice approach, is creating a static class with the expected errors. For example:

public static partial class DivisionErrors
{
    public static Error CannotDivideByZero = Error.Unexpected(
        code: "Division.CannotDivideByZero",
        description: "Cannot divide by zero.");
}

Which can later be used as following ๐Ÿ‘‡

public ErrorOr<float> Divide(int a, int b)
{
    if (b == 0)
    {
        return DivisionErrors.CannotDivideByZero;
    }

    return a / b;
}

Mediator + FluentValidation + ErrorOr ๐Ÿค

A common approach when using MediatR is to use FluentValidation to validate the request before it reaches the handler.

Usually, the validation is done using a Behavior that throws an exception if the request is invalid.

Using ErrorOr, we can create a Behavior that returns an error instead of throwing an exception.

This plays nicely when the project uses ErrorOr, as the layer invoking the Mediator, similar to other components in the project, simply receives an ErrorOr and can handle it accordingly.

Here is an example of a Behavior that validates the request and returns an error if it's invalid ๐Ÿ‘‡

public class ValidationBehavior<TRequest, TResponse>(IValidator<TRequest>? validator = null)
    : IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
        where TResponse : IErrorOr
{
    private readonly IValidator<TRequest>? _validator = validator;

    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        if (_validator is null)
        {
            return await next();
        }

        var validationResult = await _validator.ValidateAsync(request, cancellationToken);

        if (validationResult.IsValid)
        {
            return await next();
        }

        var errors = validationResult.Errors
            .ConvertAll(error => Error.Validation(
                code: error.PropertyName,
                description: error.ErrorMessage));

        return (dynamic)errors;
    }
}

Contribution ๐Ÿคฒ

If you have any questions, comments, or suggestions, please open an issue or create a pull request ๐Ÿ™‚

Credits ๐Ÿ™

  • OneOf - An awesome library which provides F# style discriminated unions behavior for C#

License ๐Ÿชช

This project is licensed under the terms of the MIT license.

error-or's People

Contributors

ahmtsen avatar amantinband avatar apfohl avatar bcamba avatar blouflashdb avatar feo2x avatar florentmsl avatar i3arnon avatar jobumble avatar kikutano avatar madhon avatar mantinbot avatar martinobordin avatar mgremy avatar mopinon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

error-or's Issues

.NET Standard 2.0 target

Is there any reason you haven't included a .NET Standard 2.0 target as there appears to be no issue compiling it for that target, would also help with more broader usage of the lib?

erroror is missing NuGet package README file

We've noticed that your package on NuGet.org is missing a README file.

Why READMEs are Important

Our customer research indicates that one of the top problems that package consumers face is insufficient package documentation such as README files. Adding a README file to your package will help users quickly understand what the package is and what it does. Since your README will be the first impression for users when they view your package on NuGet.org, it is crucial for authors to write and include high-quality READMEs for their packages.

Get Started with READMEs

How to Add a README

If you're new to NuGet READMEs, follow guidance on how to add one to your package.

How to write high quality of READMEs

Follow a blog post with README best practices and a template file to help you get started.

We Value Your Feedback

We value your feedback. If you encounter any issues or have suggestions, please reply to this issue.

Thank you for your contribution to the NuGet community.

Errors: Count = 1; IsError: False

I noticed when returning an Error.NotFound using ErrorOr, it sets the count to 1, but it does not flag IsError as true.

Function
public async Task<ErrorOr> Process

Return value
return Error.NotFound(code: "", description: $"Nothing found with the specified id: {Id}.");

image

Cannot implicitly convert type with IEnumerable

Hi Amichai, I have a problem when trying to wrap an IEnumerable to an ErrorOr<IEnumerable>:

using ErrorOr;

class Foo {
    public ErrorOr<IEnumerable<int>> wrap() {
        IEnumerable<int> nums = new List<int>(){ 1, 2 };
        return nums;   
    }
}

VSCode gives me an error:

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<int>' to 'ErrorOr.ErrorOr<System.Collections.Generic.IEnumerable<int>>' [BuberDinner.Api]csharp(CS0029)
No quick fixes available

I find a workaround: changes IEnumerable<int> tools to List<int> tools:

using ErrorOr;

class Foo {
    public ErrorOr<IEnumerable<int>> wrap() {
        List<int> nums = new List<int>(){ 1, 2 };
        return nums;   
    }
}

Consider 'Forbidden' as built-in error type

First of all, thank you very much for creating and maintaining this repository.

I would like to suggest adding an additional built-in error type, namely 'Forbidden,' because I believe that in some situations, it provides a better description than the existing 'Unauthorized.'

I understand that error types don't necessarily have to directly align with HTTP standards, but in this case, it might make more sense. This is what the specifications state:

Unauthorized

The 401 (Unauthorized) status code indicates that the request has not
been applied because it lacks valid authentication credentials for
the target resource. The server generating a 401 response MUST send
a WWW-Authenticate header field (Section 4.1) containing at least one
challenge applicable to the target resource.

If the request included authentication credentials, then the 401
response indicates that authorization has been refused for those
credentials. The user agent MAY repeat the request with a new or
replaced Authorization header field (Section 4.2). If the 401
response contains the same challenge as the prior response, and the
user agent has already attempted authentication at least once, then
the user agent SHOULD present the enclosed representation to the
user, since it usually contains relevant diagnostic information.

Forbidden:

The 403 (Forbidden) status code indicates that the server understood
the request but refuses to authorize it. A server that wishes to
make public why the request has been forbidden can describe that
reason in the response payload (if any).

If authentication credentials were provided in the request, the
server considers them insufficient to grant access. The client
SHOULD NOT automatically repeat the request with the same
credentials. The client MAY repeat the request with new or different
credentials. However, a request might be forbidden for reasons
unrelated to the credentials.

If you agree with my reasoning, I could open a PR to add Forbidden as a built-in type.

I look forward to hearing from you.

edit:
I know I can potentially add a custom error type myself, but it seemed to me like a valuable addition as a built-in

ErrorOr type cannot be deserialized

Hi @amantinband,

Thanks for this great library. I'm currently having an issue when I try to deserialize ErrorOr type. ErrorOr values seem to be serializable but not deserializable.

Sample code:

using ErrorOr;
using System.Text.Json;

ErrorOr<int> e1 = 42;
ErrorOr<int> e2 = Error.NotFound();

Console.WriteLine($"e1: {e1.Value}/{e1.IsError}");
Console.WriteLine($"e2: {e2.Value}/{e2.IsError}");

var j1 = JsonSerializer.Serialize(e1);
var j2 = JsonSerializer.Serialize(e2);

Console.WriteLine("------");

Console.WriteLine($"j1: {j1}");
Console.WriteLine($"j2: {j2}");

e1 = JsonSerializer.Deserialize<ErrorOr<int>>(j1);
e2 = JsonSerializer.Deserialize<ErrorOr<int>>(j2);

Console.WriteLine("------");

Console.WriteLine($"e1: {e1.Value}/{e1.IsError}");
Console.WriteLine($"e2: {e2.Value}/{e2.IsError}");

Output:

e1: 42/False
e2: 0/True
------
j1: {"IsError":false,"Errors":[{"Code":"ErrorOr.NoErrors","Description":"Error list cannot be retrieved from a successful ErrorOr.","Type":1,"NumericType":1}],"ErrorsOrEmptyList":[],"Value":42,"FirstError":{"Code":"ErrorOr.NoFirstError","Description":"First error cannot be retrieved from a successful ErrorOr.","Type":1,"NumericType":1}}
j2: {"IsError":true,"Errors":[{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}],"ErrorsOrEmptyList":[{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}],"Value":0,"FirstError":{"Code":"General.NotFound","Description":"A \u0027Not Found\u0027 error has occurred.","Type":4,"NumericType":4}}
------
e1: 0/False
e2: 0/False

This problem appears to be caused by getter-only properties. I think serializers are not smart enough to understand where the data comes from. I created a pull request by the way. If you have any better solution, feel free to reject it.

Thanks!

Versions
I've encountered this issue on:

ErrorOr version : 1.2.1
.NET version: .NET 7
OS version : Windows 11

Add ability to show result and errors

Unless I'm missing something, it would be nice to be able to use both value and errors. In the specific scenario I'm imagining, one would try to "BatchUpdate" via the API:

The command handler runs through and verifies that each object exists in the database exists (or not). For each item that doesn't exist in the database, generate an error, and for each item that does exist, go ahead and update. The handler then returns both the successes and failures (errors), that can be mapped to a response.

Similar to another issue, this would allow us to use IsError to set the Success to false on the response, but show errors and success values.

How do you catch error out side the domain

Hi,
many thanks for this tool,

Please see the code the below from your examples:

ErrorOr DeleteUser(Guid id)
{
var user = await _userRepository.GetByIdAsync(id);
if (user is null)
{
return Error.NotFound(code: "User.NotFound", description: "User not found.");
}

await _userRepository.DeleteAsync(user);
return Result.Deleted;

}

How do you capture an error from this line: await _userRepository.DeleteAsync(user); Using your tool so that you can include it in the returned results to the front end to inform the user that the record can not be deleted?

Many thanks
Zak

ErrorOr<ResultObj>IsError Always returns as "false"

ErrorOr<Dictionary<int, int>> priorityResult = ..... where inside I return Error.Failure("... and method returns

after checking
if (priorityResult.IsError)
return priorityResult.Errors;

IsError always false!

โŒ ErrorType NotAuthorized

Hi,
it would be nice to have a new value on the enum ErrorType to track down the NotAuthorized operations.

What is your thought? ๐Ÿ˜Š

ErrorType of Cancelled

Awesome videos and NuGet package.
It would be nice to have a cancelled error type to handle the user cancelled scenarios for APIs etc

IsError = true; Errors.Length = 0;

For some reason, when I return an empty list of Errors, I get IsError = true.

Maybe there's a better way to design this on my end, but I'd like to use ErrorOr methods to also check for null values and return null or at least IsError = false and Value = null when working with domain methods that do things like TryCreate.

Code Snippet:

/// <summary>
/// Ensures Positive Integer.
/// </summary>
public class PositiveInt : ValueObject
{
	public int Value { get; }

	public PositiveInt(int value, string propertyName)
	{
		value.Throw(paramName: propertyName).IfNegativeOrZero();
		Value = value;
	}

	/// <summary>
	/// Trys to create <see cref="PositiveInt"/>. Returns Error if it fails.
	/// <remarks>When value is null, will return IsError = true with empty list of errors.</remarks>
	/// </summary>
	/// <param name="value">Value of <see cref="PositiveInt"/>.</param>
	/// <param name="propertyName">Property <see cref="PositiveInt"/> is being assigned to.</param>
	/// <returns><see cref="PositiveInt"/> or Validation Error.</returns>
	public static ErrorOr<PositiveInt> TryCreate(int? value, string propertyName)
	{
		if (!value.HasValue)
		{
			return new List<Error>();
		}

		if (!int.IsPositive(value.Value))
		{
			return Error.Validation("PositiveInt.Invalid", $"{propertyName} must be larger than 0.");
		}

		return new PositiveInt(value.Value, propertyName);
	}

	/// <inheritdoc />
	protected override IEnumerable<object> GetEqualityComponents()
	{
		yield return Value;
	}
}

When I create a test where the value property of TryCreate is null, I get IsError = true and actual.ErrorsOrEmptyList.Should().BeEmpty() = true.

What I'd ideally like to do is just return null, but then I'd have to make the return value for the method ErrorOr<PositiveInt>?, which results in a lot of hassle. However, I also feel like I should be able to return an empty list of Error and IsError should still be false?

I'm somewhat new to this library, so maybe there's a better way to do what I'm trying to accomplish. I'm open to suggestions.

Thanks!

Change Interface to covariant IError<out TValue>

It would be great to have covariant interface instead of non-generic like this:

public interface IErrorOr<out TValue>
{
    List<Error>? Errors { get; }
    bool IsError { get; }
    List<Error> ErrorsOrEmptyList { get; }
    Error FirstError { get; }
    TValue Value { get; }
}

Then it allows us to upcast ErrorOr objects like this:

ErrorOr<TChild> childResult = new TChild();
IErrorOr<TParent> parentResult = childResult;

How do I disable ErrorOr if I set my environment to development and only enable it when it is changed to Production?

I have this code from my RecipeService.cs.

public async Task<ErrorOr<Recipe>> GetAsync(string id)
{
    var recipe = await _recipeRepo.GetAsync(id);

    if (recipe is null)
    {
        return Errors.Recipe.NotFound;
    }

    return recipe;

}

And also I have this code from my RecipeController.cs.

public async Task<IActionResult> GetRecipes([FromQuery][Required] string id)
{
    ErrorOr<Recipe> recipes = await _recipeService.GetAsync(id);

    return recipes.Match(
        recipe => Ok(recipe),
        errors => Problem(errors)
    );

}

This is my startup configuration.

var app = builder.Build();
{
    app.UseSwagger();
    app.UseSwaggerUI();

    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/error"); // I want this to activate if the environment is not in Development.
    }

    app.UseHttpsRedirection();

    app.UseAuthorization();

    app.MapControllers();

    app.Run();
}

What I want to achieve is for the REST API to return a stack trace for a specific error instead of returning a custom error. I only want it to return a custom error when I set my environment to Production.

Adding metadata to errors

Hello,

one thing FluentResults has is the ability to add metadata. After switching to ErrorOr, I have to add the metadata inside ProblemDetailsFactory, which is very cumbersome... Due to this libraries implementation I have not find a elegant way to attach metadata to the error directly.

Do you know a way to add metadata in a similar way like in FluentResults or another generic way I can use to add metadata to Error. Or is it maybe even possible to add metadata to ErrorOr (maybe via generic Error instead of hardcoded one...).

Thanks in advance ๐Ÿ˜€

[Question] Error Result from MediatR pipeline behaviour

I Have used ErrorOr for many simple apps and works really good when using MediatR. However, I am trying to implement a MediatR Pipeline Behaviour in which I log different exceptions and erros. Currently looks like this:

public class ErrorPipelineBehaviour<TRequest, TResponse>(
    ILogger<ErrorPipelineBehaviour<TRequest, TResponse>> logger) 
    : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
    where TResponse : IErrorOr

{
    private const string UnknownError = "An unknown error occurred.";

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken _)
    {
        logger.LogInformation("A request {@Request} has been sent.", request);

        try
        {
            var result = await next.Invoke();

            if (result.IsError)
            {
                var errors = result.Errors!.Select(e => new
                {
                    e.Code,
                    e.Description
                }).ToArray();
                logger.LogError("An error result has been returned for request {@Request} with errors {@Errors}.", request, errors);
            }

            return result;
        }
        catch (DbException e)
        {
            logger.LogError("A database exception has ocurred for request {@Request} with exception {@Exception}.", request, e);
            throw;
        }
        catch (TaskCanceledException)
        {
            logger.LogInformation("A task has been cancelled for request {@Request}.", request);
            throw;
        }
        catch (Exception e)
        {
            logger.LogError("An exception has ocurred for request {@Request} with exception {@Exception}.", request, e);
            throw;
        }
    }
}

I would like to, instead of throwing the exceptions, return an Error. The ways I have tried it results in either compilation errors or runtime errors (with casting).
Something like return Error.Unexpected("TaskCancelled", "The task has been cancelled."); would say "Cannot implicitly convert type 'ErrorOr.Error' to 'TResponse'".

I have also tried using the ErrorOr.From method and the ErrorOrFactory.

Is there a way currently to do something like this?

Adding IsError/Value to IErrorOr

Hi.
I am writing LoggingPipelineBehavior using MediatR.
Here's class:

internal class LoggingPipelineBehavior<TRequest, TResponse> :
        IPipelineBehavior<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
        where TResponse : IErrorOr

And here's my Handle method:

public async Task<TResponse> Handle(TRequest request, CancellationToken _, RequestHandlerDelegate<TResponse> next)
{
    LogRequest(request);
    TResponse response = await next();

    if (response is IErrorOr errorOr && errorOr.Errors?.Count > 0)
    {
        LogErrors(errorOr.Errors);
        return response;
    }

    LogResponse(response);
    return response;
}

Unfortunately, if my handler is returning successful object (not errors), I'm still going inside that if is IErrorOr block, because if ErrorOr.IsError is false, Errors's Count is still getting 1 (NoErrors error).
Would it be possible to add
bool IsError { get; }
to IErrorOr interface, so it can be checked by this flag if this object is successful or failed one?
Edit: Also, would it be possible to add
object Value { get; }
to interface to retrieve successful instance?
If there's other/better solution, please let me know.
By the way - great package!
Thanks,
Sล‚awek

[Enhancement] Add extension methods for to convert to IActionResult (mvc) IResult (minimal API)

First of all, I love this extension.
I would like to see extension methods to convert an ErrorOr (domain result) to an IActionResult or IResult.

The conversion should send 200OK or 204NoContent if the operation is successfull.
If the result is an Error the conversion would take the type of error and return an appropriate ProblemDetails with the correct status Code.

This enhancement was inspired by this repository https://github.com/AKlaus/DomainResult

Question: `ErrorOr<>.Errors` as readonly collection

Is ErrorOr designed so that you can mutate its state (List<Error> Errors), or is there any other reason for this design choice?

Using IReadOnlyList<> (or even IReadOnlyCollection<> if accessing very first error is not needed) would improve (imo):

  1. Signature - giving hint that this is immutable data structure
  2. No need for allocating new List<Error> when accessing errors (though this should be relatively cheap, but still additional allocation)
    • both caching no errors list and empty one
  3. Only drawback I can see is that to ensure no one tempers with input, copy of input list is needed (in ctor ErrorOr(List<Error> errors))

I'm really interested knowing specific reason (of course changing it to IReadOnly* is breaking, obviously)

Cannot implicitly convert type IQueryable<T> to ErrorOr.ErrorOr<IQueryable<T>>

Hi, thank You for this awesome library.
I'm having trouble implementing a get request handler, the code as follow:

public class GetQueryHandler :
    IRequestHandler<GetQuery, ErrorOr<IQueryable<User>>>
{
    private readonly IApplicationDbContext _context;

    public GetQueryHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<ErrorOr<IQueryable<User>>> Handle(
        GetQuery request,
        CancellationToken cancellationToken)
    {
        await Task.CompletedTask;
        return _context.Users.AsQueryable();
    }
}

Throws the error cannot implicitly convert type IQueryable to ErrorOr.ErrorOr<IQueryable>.
If I change the IQueryable to IList and change the return to _context.Users.ToList() it works.

Non generic ErrorOr possible?

Scattered throughout our code base I see return types of ErrorOr. I think this comes from legacy code (pre the use of ErrorOr) where the bool indicated success or failure. It strikes me that the presence/absence of errors indicates the same. Is it possible to return ErrorOr (none-generic) so I can just test the IsError status to indicate success or failure? I guess it may be an issue with how to return the discriminated union for success because it then becomes ErrorOr!

[Bug/Enhancement] ErrorOr should not "invent" a result value in case error, it should force caller to handle error instead

ErrorOr is following the Golang-style (and it is Bad) ๐Ÿ‘‡

ErrorOr<float> result = Divide(4, 0);

// I forgot to handle error here and it is the "root cause" for future troubles
// if (result.IsError) { ... }

// thing continue to work with the hidden error(s) in the result.
var nextResult = result.Value * 2;  //nextResult = 0

//... until the application crashed somewhere far away from the "root cause"
// and it is hard to trace back to the "root cause".

My suggestion: ErrorOr should follow the Rust-style instead ๐Ÿ‘‡

ErrorOr<float> result = Divide(4, 0);

// I forgot to handle error here
// if (result.IsError) { ... }

// I try to get the result (without error handling)
var nextResult = result.Unwrap() * 2;  // the Unwrap function will "crash" / "panic" here

The first example shows that ErrorOr is "inventing" a false result (result.Value = 0) and it is not logical. You can also see it as a buggy behavior.

Logically, accessing to the result.Value should crashed because the value for Divide(4, 0) simply doesn't exist.

It is what the Rust-style is doing and I demonstrated it in the second example with the imaginary Unwrap function.

In other words, the Result<T,E> of Rust beautifuly "forced" developers to handle error: In order to Get the result T you MUST to explicitly handle the "E" one way or other: in a match statement or by a simple unwrap() function to get the T which will panic the E...

Extend ErrorType enum

It would be great to have all the type of errors that could be mapped to 4xx status code series

[Question] ASP.net Is there a response type i can deserialize to?

Hello guys,
First of all, thanks for this awesome project!

Some infos about my structure:

  • ASP.net Web Api with the mentioned Api Controller:
[ApiController]
public class ApiController : ControllerBase
{
    protected IActionResult Problem(List<Error> errors)
    {
        if (errors.Count is 0)
        {
            return Problem();
        }

        if (errors.All(error => error.Type == ErrorType.Validation))
        {
            return ValidationProblem(errors);
        }

        return Problem(errors[0]);
    }

    protected IActionResult Problem(Error error)
    {
        int statusCode = error.Type switch
        {
            ErrorType.Conflict => StatusCodes.Status409Conflict,
            ErrorType.Validation => StatusCodes.Status400BadRequest,
            ErrorType.NotFound => StatusCodes.Status404NotFound,
            _ => StatusCodes.Status500InternalServerError,
        };

        return Problem(statusCode: statusCode, detail: error.Description);
    }

    protected IActionResult ValidationProblem(List<Error> errors)
    {
        ModelStateDictionary modelStateDictionary = new();

        foreach (Error error in errors)
        {
            modelStateDictionary.AddModelError(
                error.Code,
                error.Description);
        }

        return ValidationProblem(modelStateDictionary);
    }
}

my controller method:

       ... 
        return result.Match(
            feedback => Ok(feedback.Adapt<FeedbackDto>()),
            Problem);

my question is:

Is there a type where my client application (C# Console Application ) can deserialize the response to?

  • if i got a validation error the response looks like this:
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "FeedbackId": [
      "'Feedback Id' must be greater than '10'."
    ]
  },
  "traceId": "00-9289d12c9f497e60f26fd2c5ba8fcdad-0acef67b166935b1-00"
}
  • if not found:
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
  "title": "Not Found",
  "status": 404,
  "detail": "Feedback not found",
  "traceId": "00-239ea5296eadc0a80ab6c052a23ded71-df98a7806f0d9652-00"
}
  • and normal:
{
  "id": 11,
  "title": "string",
  "content": "string",
  "errorMessages": "string",
  "informReporter": true,
  "feedbackStatus": "NotReviewed",
  "feedbackType": "Bug",
  "reporter": "string",
  "reportedWhen": "2024-04-16T09:44:31.4599099",
  "appName": "string",
  "appVersion": "string",
  "lastEditedBy": "string",
  "lastEditedWhen": "2024-04-16T09:44:31.4599159"
}
  • here my simple test application
var result = await "https://localhost:44342/Feedbacks/9"
    .AllowAnyHttpStatus()
    .GetStringAsync();

var json2 = JsonSerializer.Deserialize<ErrorOr<FeedbackDto>>(result);

Is there away in my C# Console application to deserialize the response or has ErrorOr a default function to convert the response into one response with alle the same properties?

or what am i missing?

Thank you

Add Dictionary to the ErrorOr.Error type

It would be nice to have an optional dictionary for errors, next to the errorCode, message,... So it would be possible to provide addtional informations with an error.

How to Register IPipelineBehavior Validation with MediatR using ErrorOr<TResult> instead TResult

Hi,

I tried to implement MeaditR Commands/Queries Validator with ErrorOr library. Follow your documentation [https://github.com/amantinband/error-or#dropping-the-exceptions-throwing-logic] with some little changes(in bold) ValidationBehavior class is below:

public class ValidationBehavior<TRequest, TResult> : IPipelineBehavior<TRequest, ErrorOr>
where TRequest : IRequest<ErrorOr>
{
private readonly IValidator? _validator;

public ValidationBehavior(IValidator<TRequest>? validator = null)
{
    _validator = validator;
}

public async Task<ErrorOr<TResult>> Handle(
    TRequest request,
    CancellationToken cancellationToken,
    RequestHandlerDelegate<ErrorOr<TResult>> next)
{
    if (_validator == null)
    {
        return await next();
    }

    var validationResult = **await** _validator.**ValidateAsync**(request);

    if (validationResult.**IsValid**)
    {
        return validationResult.Errors
           .ConvertAll(validationFailure => Error.Validation(
               code: validationFailure.PropertyName,
               description: validationFailure.ErrorMessage));
    }

    return await next();
}

}

But above mentioned class never triggered.

Static class ConfigureServices.cs where is services registered is below:

using System.Reflection;
using FluentValidation;
using MediatR;

namespace Microsoft.Extensions.DependencyInjection;
public static class ConfigureServices
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddHttpContextAccessor();

    services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
    services.AddMediatR(Assembly.GetExecutingAssembly());

    services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

    return services;
}

}

Please can you give me advice how to properly register ValidationBehavior class ?

Thanks in advance. :-)

SUGGESTION: Add a changelog for each version released

I went from version 1.30 to version 1.90 and have to look through the commits (you luckily add the versions in a commit) to see what might have been changed or fixed.
By using Github releases, of adding a changelog.md this would be much quicker.

Smart way to use ErrorOr in a similar style to with-Expressions

This is more of a question than an issue. I use ErrorOr to guard my calculation engine from getting faulty data and it works great. I usually do this by making constructors private and my objects immutable and allow object creation only in form of

public static ErrorOr<T> TryCreate(parameters...);

I now have some cases where I would like to change one or two properties of the object. For C# record classes this is solved very elegantly by using with expresisons:

var person2 = person1 with {Lastname='Doe' };

The drawback of with-expressions is that in this case I cannot run all my validation logic.
In some cases I decided to create a copy constructor-like version of TryCreate() manually to still get an ErrorOr and all it's validation logic but writing copy constructors is not something I enjoy doing. This looks something like this:

 public static ErrorOr<MyClass> TryCreate(MyClass orig, int? id= null, string? name= null)
 {
     id ??= orig.Id;
     name ??= orig.Name;
     return TryCreate(id.Value, name.Value);
 }

// can be called with named parameters like so:
 var y = TryCreate(myClassObj, name:"Bill");

While this consuming part is okay, the writing part of the copy constructors is something I would want to avoid.

I wonder if there is a more elegant solution (which does not involve Reflections). I think it could be done with code generation but that's something I havent't done before.

would appreciate ideas on this topic.

Usage of Library to Replace null return type

Hi,

I think it's more an overall question/suggestion :-).
Was this library also created to replace null return types in my application or just to prevent to throw exceptions and get better Error Results?
Because if I would use this to prevent null return types I think the basic naming "ErrorOr" is not the best, when you read "everywhere" in your ValueObjects oder DomainLogic "ErrorOr<>" return type looks strange.

[Enhancement] Add FailIf from Task<T>

It would be cool to have a FailIf extension method for this kind of use :

await new LoginIfsQuery(
                GrantType: GrandType,
                Username: request.Username,
                Password: request.Password,
                ClientId: _ifsOptions.ClientId,
                ClientSecret: _ifsOptions.ClientSecret,
                Scope: Scope
            ).ToErrorOr()
            .ThenDoAsync(query => ifsApiService.LoginAsync(_ifsOptions.TokenUrl, query, cancellationToken))
            .FailIf(x => x is null, Error.Failure("Login.Failure"));

It's not working at this time...

[Request] Add overload without capturing errors or values in functional methods

Sometimes we simply wish to act based on the outcome, without directly handling the error or value. While the discard operator is an option, I perceive it more as a workaround.

Consider the following example:

var user = await userRepository.GetAsync(userContext.CurrentUserId, cancelationToken);
await user.ChangePassword(oldPassword, newPassword, passwordHasher)
  .ThenAsync(userRepository.UpdateAsync(user, cancelationToken));

This is a specific use case of mine, but I believe it enhances the library's design. Additionally, I demonstrated the ThenAsync method as an example, but this concept can be extended to other methods.

Thank you for your time. If you disagree with this request, I would appreciate hearing from you. Thank you!

Add error levels (Warning, Fatal, Error...)

Say that you are processing a collection of items: for each item, you need to contact an external service accessible under authentication.

There are then different levels of errors: "fatal", for errors as "authentication failed" (this error will occur for each item in the list), and "warning", such as "this item does not exist".

We should be able to handle errors based on their levels

Use Errors for SwaggerResponse attributes?

Hi, I like this library since it allows me to pull more and more logic into service classes rather than bloat my controllers. I also want my clients to be able to see possible outcomes of API endpoint calls by utilizing OpenAPI spec "Response" information. Currently I am using the SwaggerResponse attribute, but I watched your video about flow control and now I am trying to incorporate the idea of centralized errors into my app.

I was wondering if one can use the defined errors to annotate controller functions, e.g. like this:
[SwaggerResponseByError(Errors.Authentication.WrongCredentials)]

Do you think something like this can be achieved? Would you consider it sensible doing so? Thanks!

Suggestion: Adding of ErrorType.Domain (or ErrorType.UseCase or ErrorType.BusinessRule)

Hi @amantinband ,
Thank you very much for developing this library. Got to know about it from your video series.
Let me know your thoughts on adding a special error type ErrorType.Domain to represent errors related specific to business rules instead of just ErrorType.Custom. This is because, if its an Error it indeed means a type of Failure so instead of just ErrorType.Failure if we use ErrorType.Domain, it signifies failure in some use case / business rule validation.
Example, consider this business rule: User cannot add/upload more than 3 products to sell under his/her current FREE subscription.
Can you please share your thoughts on this.

[Bug] Error equality not working as intended

Hi @amantinband,
First of all I would like to thank you and all contributors for this great library :D

My team and I recently decided to upgrade our ErrorOr version from 1.2.0 to 1.9.0 for the newest features, but quickly realized that this version brakes our automated tests. We have been writing tests asserting that the correct Error is returned for invalid input. For example, if we expect an InvalidStartDepthError we provide an instance of this error in our parameterized tests as expectedError and check that occurredError.Should().BeEquivalent(expectedError).

The problem, as far as I can tell, seems to be related to how equality is handled for the Error class. After some research it appears the problem was introduced in version 1.4.0, when the "Metadata" property was added. In version 1.3.0, we could write assertions like this:

[Test]
public void Errors_Should_Be_Equal()                                // Success
{
    var notFoundError = Error.NotFound("Question.NotFound", "Question Not Found");
    var sameNotFoundError = Error.NotFound("Question.NotFound", "Question Not Found");

    notFoundError.Should().Be(sameNotFoundError);
}

[Test]
public void Errors_Should_Be_Equivalent()                           // Success
{
    var notFoundError = Error.NotFound("Question.NotFound", "Question Not Found");
    var sameNotFoundError = Error.NotFound("Question.NotFound", "Question Not Found");

    notFoundError.Should().BeEquivalentTo(sameNotFoundError);
}

and the following would be the results checking equality manually:

var isEqual = notFoundError == sameNotFoundError;                  // true
var isEqualsEqual = notFoundError.Equals(sameNotFoundError);       // true

I would argue that this is the expected behavior.

However, from version 1.4.0 and newer this equivalence no longer holds:

[Test]
public void Errors_Should_Be_Equal()                                // Error
{
    var notFoundError = Error.NotFound(
        "Question.NotFound",
        "Question Not Found",
        new Dictionary<string, object> { { "answer_id", 42 } }
    );

    var sameNotFoundError = Error.NotFound(
        "Question.NotFound",
        "Question Not Found",
        new Dictionary<string, object> { { "answer_id", 42 } }
    );

    notFoundError.Should().Be(sameNotFoundError);
}

[Test]
public void Errors_Should_Be_Equivalent()                           // Error
{
    var notFoundError = Error.NotFound(
        "Question.NotFound",
        "Question Not Found",
        new Dictionary<string, object> { { "answer_id", 42 } }
    );

    var sameNotFoundError = Error.NotFound(
        "Question.NotFound",
        "Question Not Found",
        new Dictionary<string, object> { { "answer_id", 42 } }
    );

    notFoundError.Should().BeEquivalentTo(sameNotFoundError);
}

and the manual checks:

var isEqual = notFoundError == sameNotFoundError;                  // false
var isEqualsEqual = notFoundError.Equals(sameNotFoundError);       // false

I do note that the Error class already is marked readonly record struct, which should handle immutability and value-based equality. However, as demonstrated above this does not appear to be working as intended.

Return ErrorOr<?>

Hi!

Thank you very much for developing this package :)

I have an update command in which I would like to have void-like behavior.
How can I use ErrorOr or something similar?

How can I create an Error type that has exception data?

What I want to achieve is something similar as:

problemdetails-json-exception-dev-3

from Hellang.Middleware.ProblemDetails

Now, the problem is I should only expose the exception data with certain environments so set the exception on an Error type in the repository and choose to show it in ... the ApiProblemDetailsFactory?

        {
            var response = await _orderClient.SummaryAsync(orderNumber);
            
            if(_logger.IsEnabled(LogLevel.Debug))
                _logger.LogDebug("Retrieved shipping information for order {orderNumber}", orderNumber);
            
            return _mapper.Map<Domain.Shipping.ShippingInformation.ShippingInformation>(response);
        }
        catch (ApiClientCallException apiClientCallException) when (apiClientCallException.StatusCode == 404)
        {
            if(_logger.IsEnabled(LogLevel.Information))
                _logger.LogInformation("Order {orderNumber} not found", orderNumber);
            
            return Errors.Shipping.OrderNotFound;
        }
        catch (Exception e)
        {
            if(_logger.IsEnabled(LogLevel.Error))
                _logger.LogError(e, "Failed to get shipping information for order {orderNumber}", orderNumber);
            
            return Error.Unexpected(); // Add exception here!
        }

Please add a strong name for the assembly

Hi,
Please add strong name for the assembly. Without it, in 4.x .NET projects without strong name it is not possible to use this library, getting exception:

Could not load file or assembly 'ErrorOr, Version=1.2.1.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)
System.IO.FileLoadException: Could not load file or assembly 'ErrorOr, Version=1.2.1.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)

Error Type Of Void

Thanks for your nice videos.
How i can use this if i dont have to return anything except errors?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.