SimpleResults 0.4.0-alpha

This is a prerelease version of SimpleResults.
There is a newer version of this package available.
See the version list below for details.
dotnet add package SimpleResults --version 0.4.0-alpha
NuGet\Install-Package SimpleResults -Version 0.4.0-alpha
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SimpleResults" Version="0.4.0-alpha" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SimpleResults --version 0.4.0-alpha
#r "nuget: SimpleResults, 0.4.0-alpha"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SimpleResults as a Cake Addin
#addin nuget:?package=SimpleResults&version=0.4.0-alpha&prerelease

// Install SimpleResults as a Cake Tool
#tool nuget:?package=SimpleResults&version=0.4.0-alpha&prerelease

SimpleResults

SimpleResults downloads

SimpleResults-AspNetCore downloads

SimpleResults-logo

A simple library to implement the Result pattern for returning from services.

This library was inspired by Arcadis.Result.

Operation Result Pattern

The purpose of the Result design pattern is to give an operation (a method) the possibility to return a complex result (an object), allowing the consumer to:

  • Access the result of an operation; in case there is one.
  • Access the success indicator of an operation.
  • Access the failure indicator of an operation.
  • Access the value (data) of the result if it exists.
  • Access the cause of the failure in case the operation was not successful.
  • Access an error or success message.
  • Access to a collection of error messages.

Why did I make this library?

  • I designed this library for use in the DentallApp project because no library like Arcadis.Result followed this response format:
{
    "success": true,
    "data": { "id": 1 },
    "message": "..",
    "errors": ["..", ".."]
}

I couldn't change this format because the front-end used it, so I didn't want to make a breaking change.

  • I do not want to throw exceptions for all cases.

Why don't I use exceptions?

I usually throw exceptions when developing open source libraries to alert the developer immediately that an error has occurred and must be corrected. In this case, it makes sense to me to throw an exception because the developer can know exactly where the error originated (by looking at the stack trace).

However, when I develop applications I very rarely find a case for using exceptions.

For example, I could throw an exception when a normal user enters empty fields but this does not make sense to me, because it is an error caused by the end user (he/she manages the system from the user interface). So in this case throwing an exception is useless because:

  • Stack trace included in the exception object is of no use to anyone, neither the end user nor the developer. This is not a bug that a developer should be concerned about.

  • Nobody cares where the error originated, whether it was in method X or Y, it doesn't matter.

And there are many more examples of errors caused by the end user: the email is duplicated or a password that does not comply with security policies, among others.

I only throw exceptions when the exception object is useful to someone (like a developer), otherwise, I use a result object to handle errors.

This is just my opinion, it is not an absolute truth either.

Anecdote

At work I had to implement a module to generate a report that performs a monthly comparison of income and expenses for a company, so it was necessary to create a function that is responsible for calculating the percentage of a balance per month:

Percentage.Calculate(double amount, double total);

The total parameter if it is zero, will cause a division by zero (undefined operation), however, this value was not provided by an end user, but by the income and expense reporting module, but since I did not implement this module correctly, I created a bug, so the algorithm was passing a zero value for a strange reason (I call this a logic error, caused by the developer).

Since I didn't throw an exception in the Percentage.Calculate function, it took me a couple of minutes to find out where the error originated (I didn't know that the problem was a division by zero).

If I had thrown an exception, I would have found the error very quickly, just by looking at the stack trace, oh yeah. In this case, it is very useful the exception object, for me and other developers.

Installation

Run the following command from the terminal:

dotnet add package SimpleResults --prerelease

Or you can also install the package for ASP.NET Core:

dotnet add package SimpleResults.AspNetCore --prerelease

Usage

This example is simple and is based on the EF Core introductory tutorial.

using SimpleResults;

public class BlogService
{
    private readonly BloggingContext _db;

    public BlogService(BloggingContext db)
    {
        _db = db;
    }

    public Result<CreatedId> Create(string url)
    {
        if(string.IsNullOrWhiteSpace(url))
        {
            return Result.Invalid();
        }

        var blog = new Blog { Url = "http://blogs.msdn.com/adonet" };
        _db.Add(blog);
        _db.SaveChanges();
        return Result.CreatedResource(blog.BlogId);
    }

    public Result<Blog> Read(int id)
    {
        if(id < 0)
        {
            return Result.Invalid("ID must not be negative");
        }

        var blog = _db.Blogs
            .Where(b => b.BlogId == id)
            .FirstOrDefault();

        if(blog is null)
        {
            return Result.NotFound();
        }

        return Result.Success(blog);
    }
}

This approach provides a new way to handle error without the need to use exceptions.

Integration with ASP.NET Core

You can convert the Result object to an ActionResult, such as:

using SimpleResults;

public class BlogRequest 
{ 
    public string Url { get; init; }
}

[ApiController]
[Route("[controller]")]
public class BlogController : ControllerBase
{
    private readonly BlogService _blogService;

    public BlogController(BlogService blogService)
    {
        _blogService = blogService;
    }

    [HttpGet("{id}")]
    public ActionResult<Result<Blog>> Get(int id)
    {
        return _blogService
            .Read(id)
            .ToActionResult();
    }

    [HttpPost]
    public ActionResult<Result<CreatedId>> Create([FromBody]BlogRequest request)
    {
        return _blogService
            .Create(request.Url)
            .ToActionResult();
    }
}

Samples

You can find a complete and functional example in these projects:

Contribution

Any contribution is welcome! Remember that you can contribute not only in the code, but also in the documentation or even improve the tests.

Follow the steps below:

  • Fork it
  • Create your feature branch (git checkout -b my-new-feature)
  • Commit your changes (git commit -am 'Added some feature')
  • Push to the branch (git push origin my-new-feature)
  • Create new Pull Request
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on SimpleResults:

Package Downloads
SimpleResults.AspNetCore

A simple package that translates the Result object to ActionResult or IResult.

SimpleResults.FluentValidation

A simple package that provides extension methods for the Fluent Validation library.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
3.0.0 1,537 3/17/2024
2.4.0 1,037 3/2/2024
2.3.2 958 12/30/2023
2.3.1 286 12/16/2023
2.3.0 151 12/10/2023
2.2.2 314 11/20/2023
2.2.1 195 11/12/2023
2.2.0 133 11/6/2023
2.1.0 251 10/27/2023
2.0.0 158 10/25/2023
1.1.0 131 10/24/2023
1.0.0 207 10/22/2023
0.5.0-alpha 106 10/21/2023
0.4.0-alpha 109 10/17/2023
0.3.0-alpha 149 10/16/2023
0.2.0-alpha 109 10/15/2023
0.1.2-alpha 96 10/13/2023
0.1.1-alpha 105 10/12/2023
0.1.0-alpha 120 10/12/2023