EV.Result
2.0.0
See the version list below for details.
dotnet add package EV.Result --version 2.0.0
NuGet\Install-Package EV.Result -Version 2.0.0
<PackageReference Include="EV.Result" Version="2.0.0" />
<PackageVersion Include="EV.Result" Version="2.0.0" />
<PackageReference Include="EV.Result" />
paket add EV.Result --version 2.0.0
#r "nuget: EV.Result, 2.0.0"
#:package EV.Result@2.0.0
#addin nuget:?package=EV.Result&version=2.0.0
#tool nuget:?package=EV.Result&version=2.0.0
EV.Result
REI Pattern (Result with Exception Intelligence) es un patrón que he tenido la oportunidad de desarrollar con el objetivo de contribuir a la comunidad de .NET, evolucionando el tradicional patrón Result mediante la integración del manejo de excepciones con el enfoque funcional clásico.
He diseñado esta propuesta con la humilde aspiración de unir ambos enfoques: la elegancia del Railway-Oriented Programming (ROP) junto con la inteligencia de un manejo avanzado de excepciones, compartiendo lo que he aprendido a lo largo de mi experiencia.
EV.Result es mi implementación concreta de REI - una solución que quiero poner a disposición de la comunidad para .NET, la cual he creado con el propósito de ofrecer una alternativa al manejo tradicional de excepciones, basándome en las lecciones que he ido recopilando durante mi trayectoria profesional.
El patrón que propongo retorna objetos Result que encapsulan tanto el éxito como el fallo, pero con un enfoque que busca ser útil: preservar las excepciones de forma inteligente, manteniendo toda su información hasta los niveles más profundos, mientras se conserva el control del flujo mediante ROP.
✨ Características
⚡ Type-safe: Manejo de errores en tiempo de compilación
🎨 API Fluida: Encadenamiento de operaciones con OnSuccess, OnFailure y Map
🔄 Railway-Oriented Programming (ROP): Patrón de bifurcación para flujos complejos
📋 JSON Estructurado: Convierte cadenas de excepciones complejas en JSON legible para logging, monitoreo y debugging
🎯 Result genérico: Result<T> para operaciones que retornan valores
🏷️ Conversiones implícitas: Código más limpio y expresivo
🎨 Tipos de errores: 9 tipos predefinidos para clasificación precisa
🔍 Captura detallada de excepciones: Serialización completa de inner exceptions
📊 Severidad automática: Clasificación inteligente de excepciones
🔌 Zero dependencias externas: Solo Newtonsoft.Json
🚀 .NET Standard 2.0+: Compatible con .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Xamarin, Unity y más
🔥 El PLUS que marca la diferencia:
Normalmente cuando una aplicación falla, el primer mensaje de error rara vez dice la verdad completa. La causa raíz está enterrada en las inner exceptions, por tal razón esta librería captura AUTOMÁTICAMENTE toda la cadena:
✅ Exception principal + TODAS las inner exceptions (hasta 30 niveles)
✅ Stack trace completo de cada nivel.
✅ Tipo exacto de cada excepción.
✅ Source, TargetSite y Data de cada error.
✅ Severidad calculada automáticamente.
✅ Todo serializado en JSON estructurado.
📦 Instalación
Package Manager Console
Install-Package EV.Result
.NET CLI
dotnet add package EV.Result
PackageReference
<PackageReference Include="EV.Result" Version="2.0.0" />
🚀 Inicio Rápido
1. Operación Simple sin Valor de Retorno
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class UserService
{
public Result ActivateUser(Guid userId)
{
var user = _repository.GetById(userId);
if (user == null)
{
return Result.Failure(
Error.NotFound("User.NotFound", "Usuario no encontrado")
);
}
user.Activate();
_repository.Update(user);
return Result.Success();
}
}
// Uso
var result = userService.ActivateUser(userId);
if (result.IsSuccess)
{
Console.WriteLine("Usuario activado correctamente");
}
else
{
Console.WriteLine($"Error [{result.Error.Type}]: {result.Error.Message}");
}
2. Operación con Valor de Retorno
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class ProductService
{
public Result<Product> GetProductById(Guid productId)
{
var product = _repository.GetById(productId);
if (product == null)
{
return Result.Failure<Product>(
Error.NotFound("Product.NotFound", "Producto no encontrado")
);
}
return Result.Success(product);
}
}
// Uso
var result = productService.GetProductById(productId);
if (result.IsSuccess)
{
var product = result.Value;
Console.WriteLine($"Producto: {product.Name} - ${product.Price}");
}
📚 Conceptos Básicos
Clase Error
using EV.Result.Abstractions.Errors;
using System.Collections.Generic;
public class Error
{
public static readonly Error None;
public static readonly Error NullValue;
public string Code { get; set; }
public string Message { get; set; }
public ErrorType Type { get; set; }
public List<ExceptionDetail> ExceptionDetails { get; set; }
public string ExceptionJson { get; set; }
// Métodos factory para crear errores específicos
public static Error Failure(string code, string message);
public static Error Validation(string code, string message);
public static Error NotFound(string code, string message);
public static Error Conflict(string code, string message);
public static Error Unauthorized(string code, string message);
public static Error Forbidden(string code, string message);
public static Error Timeout(string code, string message);
public static Error DependencyFailure(string code, string message);
public static Error Unknown(string code, string message);
// Métodos para crear errores desde excepciones
public static Error FromException(string code, Exception exception, ErrorType type = ErrorType.Failure, int maxLevel = 30);
public static Error TimeoutFromException(string code, Exception exception, int maxLevel = 30);
public static Error DependencyFailureFromException(string code, Exception exception, int maxLevel = 30);
public static Error UnknownFromException(string code, Exception exception, int maxLevel = 30);
public static Error FailureFromException(string code, Exception exception, int maxLevel = 30);
}
Enum ErrorType
using EV.Result.Abstractions.Errors;
public enum ErrorType
{
None = 0, // Sin error
Failure = 1, // Error genérico
Validation = 2, // Error de validación
NotFound = 3, // Recurso no encontrado
Conflict = 4, // Conflicto con estado actual
Unauthorized = 5, // No autenticado
Forbidden = 6, // No autorizado
Timeout = 7, // Timeout de operación
DependencyFailure = 8, // Fallo en dependencia externa
Unknown = 9 // Error desconocido
}
Clase Result
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
public class Result
{
public bool IsSuccess { get; }
public bool IsFailure { get; }
public Error Error { get; }
// Métodos para crear resultados sin valor
public static Result Success();
public static Result Failure(Error error);
// Métodos para crear resultados con valor
public static Result<TValue> Success<TValue>(TValue value);
public static Result<TValue> Failure<TValue>(Error error);
// Métodos helper
public static Result Create(bool condition, Error error);
public static Result<TValue> Create<TValue>(TValue value, Error error) where TValue : class;
}
Clase Result<T>
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class Result<TValue> : Result
{
public TValue Value { get; }
// Métodos fluidos
public Result<TValue> OnSuccess(Action<TValue> action);
public Result<TValue> OnFailure(Action<Error> action);
public Result<TNewValue> Map<TNewValue>(Func<TValue, TNewValue> mapper);
public TValue GetValueOrDefault(TValue defaultValue);
// Conversión implícita
public static implicit operator Result<TValue>(TValue value);
}
🛡️ Manejo de Excepciones
Captura Detallada de Excepciones
EV.Result incluye soporte integrado para capturar y serializar excepciones con todos sus detalles, incluyendo inner exceptions completas.
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
using System.Net.Http;
public class PaymentService
{
public Result<Payment> ProcessPayment(PaymentRequest request)
{
try
{
var payment = _paymentGateway.Charge(request);
return Result.Success(payment);
}
catch (TimeoutException ex)
{
// Captura automática de todos los detalles de la excepción
return Result.Failure<Payment>(
Error.TimeoutFromException("Payment.Timeout", ex)
);
}
catch (HttpRequestException ex)
{
return Result.Failure<Payment>(
Error.DependencyFailureFromException("Payment.GatewayError", ex)
);
}
catch (Exception ex)
{
return Result.Failure<Payment>(
Error.UnknownFromException("Payment.UnexpectedError", ex)
);
}
}
}
Métodos FromException
// Métodos disponibles para crear errores desde excepciones:
// Genérico - permite especificar el tipo de error
Error.FromException("Code", exception, ErrorType.Validation, maxLevel: 30);
// Específicos - tipos de error predefinidos
Error.TimeoutFromException("Code", exception);
Error.DependencyFailureFromException("Code", exception);
Error.UnknownFromException("Code", exception);
Error.FailureFromException("Code", exception);
// maxLevel: Niveles de inner exceptions a capturar (por defecto 30)
Clase ExceptionDetail
using EV.Result.Abstractions.Exceptions;
using System.Collections;
public class ExceptionDetail
{
public string ErrorLevel { get; set; } // Nivel en la cadena (1, 2, 3...)
public string ErrorMessage { get; set; } // Mensaje de la excepción
public string ErrorSource { get; set; } // Fuente del error
public string ErrorStackTrace { get; set; } // Stack trace completo
public string ErrorTargetSite { get; set; } // Método donde ocurrió
public IDictionary ErrorData { get; set; } // Data adicional
public string ExceptionType { get; set; } // Tipo completo de la excepción
public string Severity { get; set; } // Warning, Error, Critical
}
Ejemplo de JSON Serializado
Cuando captura una excepción, EV.Result genera un JSON estructurado como este:
[
{
"ErrorLevel": "1",
"ErrorMessage": "Error procesando el pago",
"ErrorSource": "PaymentGateway",
"ErrorStackTrace": "at PaymentGateway.Charge(...)",
"ErrorTargetSite": "Charge",
"ExceptionType": "PaymentException",
"Severity": "Error"
},
{
"ErrorLevel": "2",
"ErrorMessage": "Connection timeout",
"ErrorSource": "System.Net.Http",
"ErrorStackTrace": "at HttpClient.SendAsync(...)",
"ErrorTargetSite": "SendAsync",
"ExceptionType": "System.TimeoutException",
"Severity": "Warning"
}
]
🔄 Railway-Oriented Programming (ROP)
El patrón ROP permite encadenar operaciones de forma fluida, donde cada operación puede fallar o tener éxito.
Ejemplo Básico
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public Result<string> ProcessOrder(Order order)
{
return ValidateOrder(order)
.Map(o => CalculateTotal(o))
.OnSuccess(total => _logger.LogInformation($"Total calculado: {total}"))
.Map(total => ApplyDiscount(total))
.Map(total => FormatCurrency(total))
.OnFailure(error => _logger.LogError($"Error: {error.Message}"));
}
Encadenamiento Complejo
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class OrderProcessor
{
public Result<OrderConfirmation> ProcessOrder(CreateOrderRequest request)
{
return ValidateRequest(request)
.OnSuccess(r => _logger.LogInformation("Request validado"))
.Map(r => CreateOrder(r))
.OnSuccess(order => _logger.LogInformation($"Orden creada: {order.Id}"))
.Map(order => ReserveInventory(order))
.Map(order => ProcessPayment(order))
.Map(order => SendConfirmation(order))
.OnSuccess(confirmation => _logger.LogInformation("Proceso completado"))
.OnFailure(error => HandleOrderError(error));
}
private Result<CreateOrderRequest> ValidateRequest(CreateOrderRequest request)
{
if (string.IsNullOrWhiteSpace(request.CustomerId))
{
return Result.Failure<CreateOrderRequest>(
Error.Validation("Order.InvalidCustomer", "ID de cliente requerido")
);
}
if (request.Items == null || request.Items.Count == 0)
{
return Result.Failure<CreateOrderRequest>(
Error.Validation("Order.NoItems", "La orden debe tener al menos un item")
);
}
return Result.Success(request);
}
}
🎯 Conversiones Implícitas
EV.Result permite conversiones implícitas para hacer el código más limpio:
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class ProductService
{
// Conversión implícita de valor a Result<T>
public Result<Product> GetProduct(Guid id)
{
var product = _repository.GetById(id);
// Conversión implícita: Product -> Result<Product>
if (product != null)
return product; // Automáticamente se convierte a Result.Success(product)
return Result.Failure<Product>(
Error.NotFound("Product.NotFound", "Producto no encontrado")
);
}
// Uso más limpio con operador ternario
public Result<Product> GetProductSimple(Guid id)
{
var product = _repository.GetById(id);
return product != null
? product // Conversión implícita a Success
: Result.Failure<Product>(Error.NotFound("Product.NotFound", "No encontrado"));
}
}
🌐 Integración con ASP.NET Core
Extensión para Controladores
using Microsoft.AspNetCore.Mvc;
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
public static class ResultExtensions
{
public static IActionResult ToActionResult<T>(this Result<T> result)
{
if (result.IsSuccess)
{
return new OkObjectResult(result.Value);
}
return result.Error.Type switch
{
ErrorType.Validation => new BadRequestObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message,
type = result.Error.Type.ToString()
}),
ErrorType.NotFound => new NotFoundObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Conflict => new ConflictObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Unauthorized => new UnauthorizedObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Forbidden => new ObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
})
{
StatusCode = 403
},
ErrorType.Timeout => new ObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
})
{
StatusCode = 408
},
_ => new ObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message,
type = result.Error.Type.ToString()
})
{
StatusCode = 500
}
};
}
public static IActionResult ToActionResult(this Result result)
{
if (result.IsSuccess)
{
return new OkResult();
}
return result.Error.Type switch
{
ErrorType.Validation => new BadRequestObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.NotFound => new NotFoundObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Conflict => new ConflictObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Unauthorized => new UnauthorizedObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
}),
ErrorType.Forbidden => new ObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
})
{
StatusCode = 403
},
_ => new ObjectResult(new
{
error = result.Error.Code,
message = result.Error.Message
})
{
StatusCode = 500
}
};
}
}
Uso en Controladores
using Microsoft.AspNetCore.Mvc;
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{id}")]
public IActionResult GetProduct(Guid id)
{
var result = _productService.GetProductById(id);
return result.ToActionResult();
}
[HttpPost]
public IActionResult CreateProduct([FromBody] CreateProductRequest request)
{
var result = _productService.CreateProduct(request);
return result.ToActionResult();
}
[HttpPut("{id}")]
public IActionResult UpdateProduct(Guid id, [FromBody] UpdateProductRequest request)
{
var result = _productService.UpdateProduct(id, request);
return result.ToActionResult();
}
[HttpDelete("{id}")]
public IActionResult DeleteProduct(Guid id)
{
var result = _productService.DeleteProduct(id);
return result.ToActionResult();
}
}
📖 Ejemplos Completos
Ejemplo 1: Servicio de Autenticación
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class AuthService
{
private readonly IUserRepository _userRepository;
private readonly IPasswordHasher _passwordHasher;
private readonly ITokenGenerator _tokenGenerator;
private readonly ILogger<AuthService> _logger;
public Result<AuthResponse> Login(LoginRequest request)
{
return ValidateCredentials(request)
.Map(user => GenerateToken(user))
.Map(token => new AuthResponse
{
Token = token,
ExpiresIn = 3600
})
.OnSuccess(response => _logger.LogInformation("Login exitoso"))
.OnFailure(error => _logger.LogWarning($"[{error.Type}] Login fallido: {error.Code}"));
}
private Result<User> ValidateCredentials(LoginRequest request)
{
if (string.IsNullOrWhiteSpace(request.Email))
{
return Result.Failure<User>(
Error.Validation("Auth.EmailRequired", "El email es requerido")
);
}
var user = _userRepository.GetByEmail(request.Email);
if (user == null)
{
return Result.Failure<User>(
Error.Unauthorized("Auth.InvalidCredentials", "Credenciales inválidas")
);
}
if (!_passwordHasher.Verify(request.Password, user.PasswordHash))
{
return Result.Failure<User>(
Error.Unauthorized("Auth.InvalidCredentials", "Credenciales inválidas")
);
}
if (!user.IsActive)
{
return Result.Failure<User>(
Error.Forbidden("Auth.UserInactive", "Usuario inactivo")
);
}
return Result.Success(user);
}
private string GenerateToken(User user)
{
return _tokenGenerator.Generate(user.Id, user.Email, user.Roles);
}
}
Ejemplo 2: Procesamiento de Pagos
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class PaymentService
{
private readonly IOrderRepository _orderRepository;
private readonly IPaymentGateway _paymentGateway;
private readonly IEmailService _emailService;
private readonly ILogger<PaymentService> _logger;
public Result ProcessPayment(Guid orderId, PaymentMethod method)
{
return ValidateOrder(orderId)
.OnSuccess(order => _logger.LogInformation($"Procesando pago para orden {order.Id}"))
.Map(order => CreatePaymentRequest(order, method))
.Map(request => ChargePayment(request))
.OnSuccess(response => UpdateOrderStatus(orderId, "Paid"))
.OnSuccess(_ => SendConfirmationEmail(orderId))
.OnFailure(error => HandlePaymentError(orderId, error));
}
private Result<Order> ValidateOrder(Guid orderId)
{
var order = _orderRepository.GetById(orderId);
if (order == null)
{
return Result.Failure<Order>(
Error.NotFound("Order.NotFound", "Orden no encontrada")
);
}
if (order.Status == "Paid")
{
return Result.Failure<Order>(
Error.Conflict("Order.AlreadyPaid", "La orden ya fue pagada")
);
}
if (order.TotalAmount <= 0)
{
return Result.Failure<Order>(
Error.Validation("Order.InvalidAmount", "El monto de la orden es inválido")
);
}
return Result.Success(order);
}
private PaymentRequest CreatePaymentRequest(Order order, PaymentMethod method)
{
return new PaymentRequest
{
OrderId = order.Id,
Amount = order.TotalAmount,
Currency = "USD",
Method = method
};
}
private PaymentResponse ChargePayment(PaymentRequest request)
{
// Lógica de cobro con gateway de pago
return _paymentGateway.Charge(request);
}
private void UpdateOrderStatus(Guid orderId, string status)
{
var order = _orderRepository.GetById(orderId);
order.Status = status;
_orderRepository.Update(order);
}
private void SendConfirmationEmail(Guid orderId)
{
_emailService.SendPaymentConfirmation(orderId);
}
private void HandlePaymentError(Guid orderId, Error error)
{
_logger.LogError($"[{error.Type}] Pago fallido orden {orderId}: {error.Message}");
_notificationService.SendAlert($"Pago fallido: {error.Code}");
}
}
Ejemplo 3: CRUD Completo con ErrorTypes
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class ProductService
{
private readonly IProductRepository _repository;
private readonly ILogger<ProductService> _logger;
// CREATE
public Result<Product> CreateProduct(CreateProductRequest request)
{
// Validaciones
if (string.IsNullOrWhiteSpace(request.Name))
{
return Result.Failure<Product>(
Error.Validation("Product.InvalidName", "El nombre es requerido")
);
}
if (request.Price <= 0)
{
return Result.Failure<Product>(
Error.Validation("Product.InvalidPrice", "El precio debe ser mayor a cero")
);
}
// Verificar duplicados
if (_repository.ExistsByName(request.Name))
{
return Result.Failure<Product>(
Error.Conflict("Product.NameExists", "Ya existe un producto con ese nombre")
);
}
var product = new Product
{
Id = Guid.NewGuid(),
Name = request.Name,
Price = request.Price,
CreatedAt = DateTime.UtcNow
};
_repository.Add(product);
return Result.Success(product);
}
// READ
public Result<Product> GetProductById(Guid id)
{
var product = _repository.GetById(id);
return Result.Create(
product,
Error.NotFound("Product.NotFound", $"Producto con ID {id} no encontrado")
);
}
// UPDATE
public Result<Product> UpdateProduct(Guid id, UpdateProductRequest request)
{
return GetProductById(id)
.Map(product => ApplyUpdates(product, request))
.OnSuccess(product => _repository.Update(product));
}
private Product ApplyUpdates(Product product, UpdateProductRequest request)
{
if (!string.IsNullOrWhiteSpace(request.Name))
product.Name = request.Name;
if (request.Price.HasValue)
product.Price = request.Price.Value;
product.UpdatedAt = DateTime.UtcNow;
return product;
}
// DELETE
public Result DeleteProduct(Guid id)
{
return GetProductById(id)
.OnSuccess(product => ValidateCanDelete(product))
.OnSuccess(product => _repository.Delete(product.Id));
}
private Result ValidateCanDelete(Product product)
{
if (product.HasActiveOrders)
{
return Result.Failure(
Error.Conflict("Product.HasOrders", "No se puede eliminar un producto con pedidos")
);
}
return Result.Success();
}
}
🔧 Compatibilidad
.NET Standard 2.0
Esta librería está construida sobre .NET Standard 2.0, lo que garantiza la máxima compatibilidad con:
- ✅ .NET Framework 4.6.1+
- ✅ .NET Core 2.0+
- ✅ .NET 5, 6, 7, 8+
- ✅ Mono 5.4+
- ✅ Xamarin.iOS 10.14+
- ✅ Xamarin.Android 8.0+
- ✅ Unity 2018.1+
📝 Notas Importantes
Dependencias
La librería utiliza Newtonsoft.Json para la serialización de excepciones, ya que proporciona mejor compatibilidad con .NET Standard 2.0 que System.Text.Json.
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
Mejores Prácticas
- Siempre verifica IsSuccess antes de acceder a Value
if (result.IsSuccess)
{
var value = result.Value; // Seguro
}
- Usa conversiones implícitas para código más limpio
public Result<Product> GetProduct(Guid id)
{
var product = _repository.GetById(id);
return product != null ? product : Result.Failure<Product>(Error.NotFound(...));
}
- Aprovecha el encadenamiento fluido
return ValidateInput(input)
.Map(x => Transform(x))
.OnSuccess(x => LogSuccess(x))
.OnFailure(e => LogError(e));
- Captura excepciones con contexto completo
catch (Exception ex)
{
return Result.Failure<T>(
Error.FailureFromException("Operation.Failed", ex, maxLevel: 30)
);
}
📄 Licencia
Copyright © 2025 EV. Todos los derechos reservados.
| Product | Versions 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.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. |
-
.NETStandard 2.0
- Newtonsoft.Json (>= 13.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
REI Pattern (Result with Exception Intelligence) es un patrón que he tenido la oportunidad de desarrollar con el objetivo de contribuir a la comunidad de .NET, evolucionando el tradicional patrón Result mediante la integración del manejo de excepciones con el enfoque funcional clásico.
He diseñado esta propuesta con la humilde aspiración de unir ambos enfoques: la elegancia del Railway-Oriented Programming (ROP) junto con la inteligencia de un manejo avanzado de excepciones, compartiendo lo que he aprendido a lo largo de mi experiencia.
EV.Result es mi implementación concreta de REI - una solución que quiero poner a disposición de la comunidad para .NET, la cual he creado con el propósito de ofrecer una alternativa al manejo tradicional de excepciones, basándome en las lecciones que he ido recopilando durante mi trayectoria profesional.
El patrón que propongo retorna objetos Result que encapsulan tanto el éxito como el fallo, pero con un enfoque que busca ser útil: preservar las excepciones de forma inteligente, manteniendo toda su información hasta los niveles más profundos, mientras se conserva el control del flujo mediante ROP.
✨ Características principales:
🎯 Patrón Result T - Manejo funcional de errores sin excepciones para flujos más limpios.
🔄 Railway-Oriented Programming - Encadenamiento fluido con OnSuccess, OnFailure y Map.
🏷️ 9 Tipos de Error Clasificados - Validation, NotFound, Conflict, Unauthorized, Forbidden, Timeout, DependencyFailure, Unknown y Failure con mapeo directo a HTTP status codes.
🔍 Captura Profunda de Excepciones - El diferenciador clave: serializa TODA la cadena exception + innerException hasta 30 niveles con stack traces completos, tipos, mensajes y metadata.
📊 Severidad Automática - Clasifica excepciones en Warning/Error/Critical automáticamente.
📋 JSON Estructurado - Convierte cadenas de excepciones complejas en JSON legible para logging, monitoreo y debugging.
⚡ Type-Safe - El compilador te obliga a manejar errores, eliminando olvidos.
🎨 API Fluida - Código más limpio y expresivo con métodos de transformación.
🔌 Zero Dependencias Externas - Máximo rendimiento.
🚀 .NET Standard 2.0+ - Compatible con .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Xamarin, Unity y más.
🔥 El plus que marca la diferencia:
Normalmente cuando una aplicación falla, el primer mensaje de error rara vez dice la verdad completa. La causa raíz está enterrada en las inner exceptions, por tal razón esta librería captura AUTOMÁTICAMENTE toda la cadena:
✅ Exception principal + TODAS las inner exceptions (hasta 30 niveles)
✅ Stack trace completo de cada nivel
✅ Tipo exacto de cada excepción
✅ Source, TargetSite y Data de cada error
✅ Severidad calculada automáticamente
✅ Todo serializado en JSON estructurado