Skip to content

.NET class library for Operation Result pattern

License

Notifications You must be signed in to change notification settings

markmnl/opresult

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

build-test

FalconWare - Error Handling

Package provides OpResult class to represent the result of an operation successful or not. See original blog post on the Operation Result pattern in C# .NET here

Improve your error handling by enforcing:

  • Checking the result of an operation
  • Handling success and non-success code paths
  • Improving code readability

Usage

After adding this NuGet package e.g.

dotnet add package FalconWare.ErrorHandling

Handle result of an operation by first checking the result's WasSuccess then accessing the result Value if successful, for example:

var pokemonName = "squirtle";
var result = await TryGetPokemonBmiAsync(pokemonName);
if (!result.WasSuccess)
{
    // handle the failure
    _logger.LogError($"Failed to get {pokemonName} BMI: {result.NonSuccessMessage}");
}
else
{
    // do something with the return value
    var bmi = result.Value;
    if (bmi < 18.5)
    {
        Console.WriteLine($"{pokemonName} is underweight!");
    }
    else if (bmi < 24.9)
    {
        Console.WriteLine($"{pokemonName} within normal range");
    }
    else
    {
        Console.WriteLine($"{pokemonName} is overweight!");        
    }
}

Implement your operations to return an OpResult<T> so callers try the operation and use the result without having to write try{} catch{} blocks for operations that can fail - leaving the onus of catching exceptions in the implementation, for example:

public async Task<OpResult<float>> TryGetPokemonBmiAsync(string name)
{
    // query the pokeapi for pokemon with name supplied
    string jsonString;
    try
    {
        var response = await _httpClient.GetAsync($"pokemon/{name}");
        if (response.IsSuccessStatusCode)
        {
            jsonString = await response.Content.ReadAsStringAsync();
        }
        else
        {
            return OpResultFactory.CreateFailure<float>($"Failed getting pokemon '{name}', HTTP status code: {response.StatusCode}");
        }
    }
    catch (HttpRequestException ex) 
    {
        return OpResultFactory.CreateFailure<float>(ex);
    }

    // parse pokemon json
    JObject pokemon;
    try
    {
        pokemon = JObject.Parse(jsonString);
    }
    catch (JsonReaderException jre)
    {
        return OpResultFactory.CreateFailure<float>(jre);
    }

    // try extract height and weight, calc bmi and return that
    try
    {
        var height = pokemon["height"].Value<float>();
        var weight = pokemon["weight"].Value<float>();
        if (height < 1.0f)
        {
            return OpResultFactory.CreateFailure<float>("Failed to parse pokemon - height cannot be less than 1");
        }
        if (weight < 1.0f)
        {
            return OpResultFactory.CreateFailure<float>("Failed to parse pokemon - weight cannot be less than 1");
        }
        var bmi = weight / (height * height);
        return OpResultFactory.CreateSuccess(bmi);
    }
    catch (NullReferenceException nre)
    {
        var msg = $"Failed parse pokemon response height or weight missing: {nre.Message}";
        return OpResultFactory.CreateFailure<float>(msg);
    }
    catch (FormatException fe) 
    {
        var msg = $"Failed parse pokemon response height or weight: {fe.Message}";
        return OpResultFactory.CreateFailure<float>(msg);
    }
}