EV.Result
2.0.1
dotnet add package EV.Result --version 2.0.1
NuGet\Install-Package EV.Result -Version 2.0.1
<PackageReference Include="EV.Result" Version="2.0.1" />
<PackageVersion Include="EV.Result" Version="2.0.1" />
<PackageReference Include="EV.Result" />
paket add EV.Result --version 2.0.1
#r "nuget: EV.Result, 2.0.1"
#:package EV.Result@2.0.1
#addin nuget:?package=EV.Result&version=2.0.1
#tool nuget:?package=EV.Result&version=2.0.1
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
🌐 Integración ASP.NET Core: Extensión ToApiResponse para convertir automáticamente Result<T> en respuestas HTTP estandarizadas
📋 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.1" />
🚀 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}");
}
3. Integración con ASP.NET Core WebAPI
using EV.Result.Abstractions.Extensions;
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ProjectName.WebApi.Models;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
private readonly ILogger<ProductsController> _logger;
public ProductsController(IProductService productService, ILogger<ProductsController> logger)
{
_productService = productService;
_logger = logger;
}
[HttpGet("{id}")]
public ActionResult<ApiResponse<Product>> GetProduct(Guid id)
{
var result = _productService.GetProductById(id);
return result.ToApiResponse(this, _logger);
}
[HttpPost]
public ActionResult<ApiResponse<Product>> CreateProduct([FromBody] CreateProductRequest request)
{
if (!ModelState.IsValid)
{
return ResultToApiResponseExtension.FromModelState<Product>(ModelState, this, _logger);
}
var result = _productService.CreateProduct(request);
return result.ToApiResponse(this, _logger);
}
[HttpPut("{id}")]
public ActionResult<ApiResponse<Product>> UpdateProduct(Guid id, [FromBody] UpdateProductRequest request)
{
var result = _productService.UpdateProduct(id, request);
return result.ToApiResponse(this, _logger);
}
[HttpDelete("{id}")]
public ActionResult<ApiResponse<bool>> DeleteProduct(Guid id)
{
var result = _productService.DeleteProduct(id);
return result.ToApiResponse(this, _logger);
}
}
Respuestas generadas automáticamente:
// Éxito (200 OK)
{
"success": true,
"statusCode": 200,
"message": "Request successful",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"name": "Laptop",
"price": 999.99
},
"timestamp": "2025-11-09T10:30:00Z"
}
// Error de validación (400 Bad Request)
{
"success": false,
"statusCode": 400,
"message": "El precio debe ser mayor a cero",
"data": null,
"timestamp": "2025-11-09T10:30:00Z"
}
// No encontrado (404 Not Found)
{
"success": false,
"statusCode": 404,
"message": "Producto no encontrado",
"data": null,
"timestamp": "2025-11-09T10:30:00Z"
}
📚 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);
}
🌐 Integración ASP.NET Core
Clase ApiResponse<T>
Modelo de respuesta estandarizado para APIs REST:
using System;
namespace ProjectName.WebApi.Models
{
public class ApiResponse<T>
{
public bool Success { get; set; }
public int StatusCode { get; set; }
public string Message { get; set; }
public T Data { get; set; }
public DateTime Timestamp { get; set; }
// Métodos factory
public static ApiResponse<T> Ok(T data, string message = null);
public static ApiResponse<T> Created(T data, string message = null);
public static ApiResponse<T> NotFound(string message);
public static ApiResponse<T> BadRequest(string message);
public static ApiResponse<T> Conflict(string message);
public static ApiResponse<T> Error(string message, int statusCode = 500);
}
}
Extensión ToApiResponse
La extensión ToApiResponse convierte automáticamente objetos Result<T> en respuestas HTTP apropiadas con el formato ApiResponse<T>:
using EV.Result.Abstractions.Extensions;
using EV.Result.Abstractions.Results;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
public static class ResultToApiResponseExtension
{
// Convierte Result<T> en ActionResult<ApiResponse<T>>
public static ActionResult<ApiResponse<T>> ToApiResponse<T>(
this Result<T> result,
ControllerBase controller,
ILogger logger = null);
// Convierte ModelStateDictionary en ActionResult<ApiResponse<T>>
public static ActionResult<ApiResponse<T>> FromModelState<T>(
ModelStateDictionary modelState,
ControllerBase controller,
ILogger logger = null);
}
Mapeo Automático de ErrorType a HTTP Status
La extensión mapea automáticamente los tipos de error a códigos de estado HTTP:
| ErrorType | HTTP Status | Descripción |
|---|---|---|
| None | 200 OK | Sin error |
| Validation | 400 Bad Request | Error de validación |
| NotFound | 404 Not Found | Recurso no encontrado |
| Conflict | 409 Conflict | Conflicto con estado actual |
| Unauthorized | 401 Unauthorized | No autenticado |
| Forbidden | 403 Forbidden | No autorizado |
| Timeout | 408 Request Timeout | Timeout de operación |
| DependencyFailure | 503 Service Unavailable | Fallo en dependencia externa |
| Failure | 500 Internal Server Error | Error genérico |
| Unknown | 500 Internal Server Error | Error desconocido |
Ejemplo Completo de Controller
using EV.Result.Abstractions.Extensions;
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using ProjectName.WebApi.Models;
using System;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpGet("{id}")]
public async Task<ActionResult<ApiResponse<UserDto>>> GetUser(Guid id)
{
var result = await _userService.GetUserByIdAsync(id);
return result.ToApiResponse(this, _logger);
}
[HttpPost]
public async Task<ActionResult<ApiResponse<UserDto>>> CreateUser([FromBody] CreateUserRequest request)
{
if (!ModelState.IsValid)
{
return ResultToApiResponseExtension.FromModelState<UserDto>(ModelState, this, _logger);
}
var result = await _userService.CreateUserAsync(request);
return result.ToApiResponse(this, _logger);
}
[HttpPut("{id}")]
public async Task<ActionResult<ApiResponse<UserDto>>> UpdateUser(Guid id, [FromBody] UpdateUserRequest request)
{
var result = await _userService.UpdateUserAsync(id, request);
return result.ToApiResponse(this, _logger);
}
[HttpDelete("{id}")]
public async Task<ActionResult<ApiResponse<bool>>> DeleteUser(Guid id)
{
var result = await _userService.DeleteUserAsync(id);
return result.ToApiResponse(this, _logger);
}
[HttpPost("{id}/activate")]
public async Task<ActionResult<ApiResponse<bool>>> ActivateUser(Guid id)
{
var result = await _userService.ActivateUserAsync(id);
return result.ToApiResponse(this, _logger);
}
}
Beneficios de la Integración
✅ Respuestas consistentes: Todas las APIs devuelven el mismo formato estandarizado
✅ Mapeo automático: Los tipos de error se convierten automáticamente en códigos HTTP apropiados
✅ Logging integrado: Registra automáticamente información relevante en cada operación
✅ Manejo de ModelState: Convierte errores de validación de ASP.NET Core en respuestas consistentes
✅ Type-safe: Mantiene los tipos genéricos en toda la cadena de respuesta
✅ Menos boilerplate: Elimina código repetitivo en los controllers
🛡️ 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 = _gateway.Charge(request);
return Result.Success(payment);
}
catch (HttpRequestException ex)
{
return Result.Failure<Payment>(
Error.DependencyFailureFromException("Payment.GatewayError", ex, maxLevel: 30)
);
}
catch (TimeoutException ex)
{
return Result.Failure<Payment>(
Error.TimeoutFromException("Payment.Timeout", ex, maxLevel: 30)
);
}
catch (Exception ex)
{
return Result.Failure<Payment>(
Error.FailureFromException("Payment.UnexpectedError", ex, maxLevel: 30)
);
}
}
}
JSON de Excepciones
Cuando una excepción es capturada, EV.Result genera automáticamente un JSON estructurado:
{
"code": "Payment.GatewayError",
"message": "Error al procesar el pago",
"type": "DependencyFailure",
"exceptionJson": "[{\"Level\":0,\"Type\":\"HttpRequestException\",\"Message\":\"Connection refused\",\"Source\":\"System.Net.Http\",\"StackTrace\":\"...\",\"Severity\":\"High\",\"InnerException\":{\"Level\":1,\"Type\":\"SocketException\",\"Message\":\"No connection could be made\",\"Severity\":\"Critical\"}}]"
}
🎯 Casos de Uso Avanzados
Ejemplo 1: Autenticación JWT
using EV.Result.Abstractions.Results;
using EV.Result.Abstractions.Errors;
using System;
public class AuthenticationService
{
private readonly IUserRepository _userRepository;
private readonly IPasswordHasher _passwordHasher;
private readonly ITokenGenerator _tokenGenerator;
public Result<AuthToken> Login(string email, string password)
{
return ValidateCredentials(email, password)
.Map(user => GenerateToken(user));
}
private Result<User> ValidateCredentials(string email, string password)
{
if (string.IsNullOrWhiteSpace(email))
{
return Result.Failure<User>(
Error.Validation("Auth.InvalidEmail", "El email es requerido")
);
}
var user = _userRepository.GetByEmail(email);
if (user == null)
{
return Result.Failure<User>(
Error.Unauthorized("Auth.InvalidCredentials", "Credenciales inválidas")
);
}
if (!_passwordHasher.Verify(password, user.PasswordHash))
{
return Result.Failure<User>(
Error.Unauthorized("Auth.InvalidCredentials", "Credenciales inválidas")
);
}
if (!user.IsActive)
{
return Result.Failure<User>(
Error.Forbidden("Auth.AccountDisabled", "La cuenta está deshabilitada")
);
}
return Result.Success(user);
}
private AuthToken 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)
{
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)
);
}
- Usa ToApiResponse en tus controllers para respuestas consistentes
[HttpGet("{id}")]
public ActionResult<ApiResponse<Product>> GetProduct(Guid id)
{
var result = _productService.GetProductById(id);
return result.ToApiResponse(this, _logger);
}
- Valida ModelState con FromModelState
if (!ModelState.IsValid)
{
return ResultToApiResponseExtension.FromModelState<Product>(ModelState, this, _logger);
}
📈 Novedades en v2.0.1
✨ Nueva Extensión ToApiResponse
Se ha agregado la extensión ToApiResponse que permite una integración perfecta con ASP.NET Core WebAPI:
- Conversión automática:
Result<T>se convierte automáticamente enActionResult<ApiResponse<T>> - Mapeo inteligente: Los
ErrorTypese mapean automáticamente a códigos HTTP apropiados - Logging integrado: Registra automáticamente información relevante según el tipo de resultado
- Manejo de ModelState: Método
FromModelStatepara convertir errores de validación ASP.NET Core - Respuestas estandarizadas: Todas las APIs devuelven el mismo formato con
ApiResponse<T>
Beneficios de v2.0.1
✅ Elimina código boilerplate en controllers
✅ Garantiza consistencia en todas las respuestas de la API
✅ Simplifica el manejo de errores en endpoints
✅ Mejora la experiencia de desarrollo con menos código repetitivo
✅ Facilita la integración con Clean Architecture
📄 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
- Microsoft.AspNetCore.Mvc.Core (>= 2.3.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