Kentico Xperience Design Patterns: Handling Failures - Return Errors, Don't Throw Them
Tags: xperience, kentico, aspnetcore, csharp
Published: 2022-03-14 15:03:42

---
title: Kentico Xperience Design Patterns: Handling Failures - Return Errors, Don't Throw Them
published: true
description: Learn the problems with modeling errors as exceptions and why we should return errors from our C# methods instead
tags: xperience, kentico, aspnetcore, csharp
series: Kentico Xperience Design Patterns
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hcusgcjdsp4aozej50g9.jpg
---

In a previous post we looked at how to centralize exception handling in a Kentico Xperience ASP.NET Core application by using out-of-the-box middleware. This enabled us to handle errors as a cross-cutting concern and [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself) up our code 👍🏽.

However, the approach had some drawbacks. Namely, the method signatures of our code became dishonest - we had to assume that any method could throw an exception 😒.

Centralized exception handling, while not bad in itself, can encourage using exception handling as control flow, which can make code much harder to reason about when reading it. This handling also tends to occur in an area of our application where it's too "late" to gracefully respond to the failure, so the best we can do is log it an display a generic error page 😔.

Below we'll look at an alternative to throwing exceptions - returning errors from methods 😮.

> Note: This is part 2 of a 3 part series on handling failures.

> [Part 1](https://dev.to/seangwright/kentico-xperience-design-patterns-handling-failures-centralized-exception-handling-4p3n) and [Part 3](https://dev.to/seangwright/kentico-xperience-design-patterns-handling-failures-the-result-monad-1j25)

## 📚 What Will We Learn?

- [What Do We Do With A Failure?](#what-to-do)
- [Prior Art: Go](#prior-art)
- [Returning Errors](#returning-errors)
- [Better Method Signatures](#better-method-signatures)

## [What Do We Do With A Failure?](#what-to-do)

One important question to ask, before we proceed further, is "What do we do with a failure?"

In some applications the answer is simple - nothing! We will try to catch Exceptions in a central place in the application, log them to the [Xperience Event Log](https://docs.xperience.io/13api/configuration/event-log), and let middleware render an error page ([YOLO!](https://www.youtube.com/watch?v=UKqDStuh4fM) 🤘🏾).

However, in more complex situations we might need to handle errors at the method call site so we could provide fallback operations or data to ensure as much of a Page's content is rendered as possible.

If we are using Exceptions to communicate failures, we're going to end up with `try`/`catch` logic scattered around each of these method call sites, which is why we adopted centralized error handling in the previous post 🤦🏻‍♀️.

The alternative to this `try`/`catch` spaghetti is to "return" the error to the caller, handling any exceptions or failures within the method being called 🤨.

## [Prior Art: Go](#prior-art)

We can look to other languages for inspiration here, since some of them, like [Go](https://go.dev/blog/error-handling-and-go) and [Rust](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html), treat exceptions as truly exceptional events and instead return errors from functions when there are logic violations and other failures 🤓.

Go treats `string` values as the standard error type that a function might return in case of a failure, so it's common to see a function defined as follows:

```go
func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, fmt.Errorf("square root of negative number %g", f)
    }
    // implementation

    return val, nil
}
```

In Go, the function return type (`(float64, error)`) comes after the parameter list. In this case, it shows `Sqrt` will always return 2 values (which is sometimes called a [couple or a tuple](https://en.wikipedia.org/wiki/Tuple)). The first item returned is "value" and the second is the "error".

Looking at the function body, we can see the values we return when the input is invalid (`f < 0`):

- `0` - the results of the square root operation
- `fmt.Errorf(...)` which returns a `string` (as the type `error`)

The value is a "default" and the error is a helpful message 😉.

In the successful case at the end of the function we return `val` as the calculated value and `nil` representing no errors.

Any code that would call `Sqrt` would check if any errors had been returned before processing the result:

```go
f, err := Sqrt(-1)

if err != nil {
    fmt.Println(err)
}
```

Even though Go treats `string` values as the standard error type, developers can create their own custom errors that contain more context. Callers of a function can then react to this contextual error information 🧐.

Every function that can result in a failure follows this pattern, which means a typical Go program has lots of `if err != nil { ... }` blocks in it. This might lead us to ask ourselves, "What's the benefit over exceptions if we still have to repeat guards/checks everywhere?" 😕

Well, the main benefit is that the function signatures tell the call exactly what to expect. There's no surprises here! 👏🏿

If your application can't ever produce errors, then don't worry, you won't need many error returning functions. If it can experience experience errors then they should be modeled and handled 👍🏼.

⚠ **Don't confuse an application that doesn't handle errors with an application that doesn't have them.** ⚠

## [Returning Errors](#returning-errors)

Let's look at an example scenario for a Kentico Xperience application.

In a `HomeController` that builds a view model for our Home Page, we need to retrieve content from the database, which we will do with a `HomePageService` class:

```csharp
public class HomeController : Controller
{
    private readonly HomePageService service;
    private readonly IEventLogService log;

    // ...

    public async Task<ActionResult> Index(CancellationToken token)
    {
        ??? = service.GetData();

        return View(new HomeViewModel(???));
    }
}
```

Before we call the `HomePageService`, we need to figure out how to design the `GetData` method so that we don't have to worry about catching exceptions.

First, we'll define the method signature to return a [C# Tuple](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples), which is very similar to Go's multiple-value returns for functions that can have failures.

> Tuples provide "concise syntax to group multiple data elements in a lightweight data structure".

```csharp
public class HomePageService
{
    private readonly IPageRetriever pageRetriever;
    private readonly IPageUrlRetriever urlRetriever;
    private readonly IPageDataContextRetriever contextRetriever;

    // ...

    public async Task<(HomePageData? data, string? error)> GetData(CancellationToken token)
    {
        // ...
    }
}
```

Now for the implementation of our `GetData` method:

```csharp
public async Task<(HomePageData? data, string? error)> GetData(CancellationToken token)
{
    if (!retriever.TryRetrieve<HomePage>(out var context))
    {
        return (null, "Could not get HomePage Page context");
    }

    var products = Enumerable.Empty<ProductPage>();

    try 
    {
        products = await pageRetriever<ProductPage>
            .RetrieveAsync(
                q => q.WhereTrue("ProductPageIsFeatured"),
                b => b.Key("HomePageFeaturedProducts"),
                cancellationToken: token);
    }
    catch (Exception ex)
    {
        return (null, $"Query for featured products failed: {ex}");
    }

    var featured = new List<FeaturedProduct>();

    foreach (var product in products)
    {
        var url = urlRetriever.Retrieve(product);

        if (url is null)
        {
            continue;
        }

        featured.Add(new FeaturedProduct(product, url));
    }

    return (new HomePageData(products, context.Page), null);       
}
```

The caller (`HomeController.Index`) can now decide what to do if there are any errors present in the returned Tuple:

```csharp
public async Task<ActionResult> Index(CancellationToken token)
{
    var (data, error) = await service.GetData();

    if (error is not null or data is null)
    {
        log.LogError(
           "HomeController",
           "DATA_FAILURE",
           error);

        return ServerError(500);
    }

    return View(new HomeViewModel(data));
}
```

## [Better Method Signatures](#better-method-signatures)

If the C# Tuple seems a little awkward, we could instead try a C# 10 [record struct](https://nietras.com/2021/06/14/csharp-10-record-struct/):

> We choose a `record struct` because it has all the low memory benefits of `struct` types and the auto-generated members of `record` types while also having a very simple definition 😍.

```csharp
public readonly record struct Result<T>(T? Value, string? Error)
{
    public static Result<T> Value(T value) => 
        new Result<T>(value, null);
    public static Result<T> Error(string error) => 
        new Result<T>(null, error);
}
```

This type is pretty simple, but that's really all we want (for now) if we are trying to return errors but avoid Tuples.

We can update our `HomePageService.GetData()` method to the following:

```csharp
public async Task<Result<HomePageData>> GetData(CancellationToken token)
{
    if (!retriever.TryRetrieve<HomePage>(out var context))
    {
        return Result<HomePageData>.Error(
            "Could not get HomePage Page context");
    }

    var products = Enumerable.Empty<ProductPage>();

    try 
    {
        products = await pageRetriever<ProductPage>
            .RetrieveAsync(
                q => q.WhereTrue("ProductPageIsFeatured"),
                b => b.Key("HomePageFeaturedProducts"),
                cancellationToken: token);
    }
    catch (Exception ex)
    {
        return Result.Error<HomePageData>(
            $"Query for featured products failed: {ex}");
    }

    var featured = new List<FeaturedProduct>();

    foreach (var product in products)
    {
        var url = urlRetriever.Retrieve(product);

        if (url is null)
        {
            continue;
        }

        featured.Add(new FeaturedProduct(product, url));
    }

    var data = new HomePageData(products, context.Page);
    return Result<HomePageData>.Value(data); 
}
```

We still have to make the same checks in our `HomeController`, but at least the method signature of our service has been cleaned up 🧹 and the creation of "values" and "errors" returns from our method is more explicit 💪.

> `record struct` types include a `Deconstruct` method which adds the [deconstruction syntax](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#user-defined-types) that Tuples have. This means our custom `Result` type can use the same syntax as the tuple in our `HomeController`.

## Conclusion

We've reviewed the problems that come with handling errors in our application with only global exception handling. We end up with dishonest method signatures that we can't trust and cannot respond to errors where we need to 😑.

By following the pattern of returning errors, like the programming languages Go and Rust, we end up with a much more understandable and readable code base. We encourage modeling the potential error states of our application, giving ourselves the opportunity to handle them 😄.

While the C# tuple syntax has its uses, using it to model application errors might not be the best. So instead we can create a simple container type - `Result` - to make our method signatures more legible 😀. Unfortunately this still leaves us with a bunch of procedural `if (error is not null ...) { ... }` checks in our application 😣.

In my next post, we'll look at a way of returning failures so we can respond to them intelligently, while avoiding both complex method signatures and procedural conditional checks everywhere by using the `Result` [monad](https://en.wikipedia.org/wiki/Monad_(functional_programming)) 🤔.
...

As always, thanks for reading 🙏!

## References

- [Don't Repeat Yourself](https://en.wikipedia.org/wiki/Don't_repeat_yourself)
- [Xperience Event Log API Examples](https://docs.xperience.io/13api/configuration/event-log)
- [Go Docs: Error handling and Go](https://go.dev/blog/error-handling-and-go)
- [Rust Docs: Recoverable Errors with Result](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
- [Wikipedia: Tuple](https://en.wikipedia.org/wiki/Tuple)
- [C# Docs: Tuple types](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples)
- [C# 10 Record Struct](https://nietras.com/2021/06/14/csharp-10-record-struct/)
- [C# Deconstruct Custom Types](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/deconstruct#user-defined-types)
- [Wikipedia: Monad](https://en.wikipedia.org/wiki/Monad_(functional_programming))

---

<figcaption>Photo by <a href="https://unsplash.com/@jordanmadrid">Jordan Madrid
</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Bits of Xperience](https://dev.to/seangwright/series/8740)