The Result Pattern in .NET

Return success or failure results in your .net applications elegantly.

ebeeraheem

View Profile
39 views
Jun 20, 2025
Updated Jun 21, 2025

Start by defining the Error and Result models

// Error.cs
public record Error(string Code, string Message);

// Result.cs
public class Result<T> : Result
{
   [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
   public T Value { get; }
   
   [JsonConstructor]
   private Result(bool isSuccess, T value, Error error) : base(isSuccess, error)
   {
       Value = value;
   }
   
   public static Result<T> Success(T value) => new(true, value, null!);
   public static Result<T> Failure(Error error, bool isSuccess = false) =>
       new(isSuccess, default!, error);
}

public class Result
{
   public bool IsSuccess { get; }
   public bool IsFailure => !IsSuccess;
   
   [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
   public Error Error { get; }
   
   [JsonConstructor]
   protected Result(bool isSuccess, Error error)
   {
       IsSuccess = isSuccess;
       Error = error;
   }
   
   public static Result Success() => new(true, null!);
   public static Result Failure(Error error) => new(false, error);
}

Note that the generic result type Result<T> is only required if you want to return data

Define static Error classes based on your application needs. For example:

public static class UserErrors
{
   public static Error NotFound =>
       new("NotFound", "The requested user was not found.");
    public static Error ProfileUpdateFailed =>
       new("ProfileUpdateFailed", "Failed to update user profile. Please try again later.");
}

Then use your newly found superpowers like so

if (user is null)
{
       logger.LogWarning("User not found for changing password: {UserId}", userId);
       return Result.Failure(UserErrors.NotFound);
}
// ...
logger.LogInformation("Password changed successfully for user ID: {UserId}", userId);
return Result.Success();

If you would like to return additional data, then…

return Result<UserProfileDto>.Success(user);

I'd take this over exceptions for flow-control any day. Exceptions have their place; this is not it.