Pitasoft.Result
7.1.2
See the version list below for details.
dotnet add package Pitasoft.Result --version 7.1.2
NuGet\Install-Package Pitasoft.Result -Version 7.1.2
<PackageReference Include="Pitasoft.Result" Version="7.1.2" />
<PackageVersion Include="Pitasoft.Result" Version="7.1.2" />
<PackageReference Include="Pitasoft.Result" />
paket add Pitasoft.Result --version 7.1.2
#r "nuget: Pitasoft.Result, 7.1.2"
#:package Pitasoft.Result@7.1.2
#addin nuget:?package=Pitasoft.Result&version=7.1.2
#tool nuget:?package=Pitasoft.Result&version=7.1.2
Pitasoft.Result
English
Pitasoft.Result is a .NET library designed to standardize responses from REST services and internal application layers. It provides a robust set of classes to wrap data, status codes, and error collections, facilitating a unified communication contract between APIs, services, and clients.
Features
- Standardized Responses: Unify your API outputs using
Result,ResultEntity<T>,ResultEntities<T>, andResultBatch<T>. - Fluent Error Handling: Easily add validation errors, exceptions, or business rule violations using a fluent interface.
- Rich Status Management: Built-in
StatusResultenum covering common API scenarios (Success, Not Found, Validation Errors, Database Errors, etc.). - Batch Operations: Specialized support for processing multiple items with
ResultBatch<T>. - Pagination Support:
ResultEntities<T>includes metadata like total count, and the library providesPagingParametersfor request handling. - Async Support:
MatchAsyncandMapAsyncfor chaining async operations cleanly. - Implicit Conversions: Convert
StatusResultor entities directly to result types with zero boilerplate. - Deconstruction: Use C# deconstruction to extract values and status easily:
var (status, user) = result;. - Extensions: Useful helper methods to check for success, specific errors, or state.
- Functional API:
Match,Map,Tap,Bind, andEnsurefor fluent result processing. - Combine Results: Aggregate multiple results into one with
Result.Combine(). - Performance: High-performance implementation with minimal allocations and
AggressiveInlining.
Installation
dotnet add package Pitasoft.Result
Quick start
// 1) Simple result
Result r1 = Result.Ok();
Result r2 = Result.ValidationError().AddError("Email", "Invalid");
// 2) Entity
ResultEntity<User> u = Result.Ok(new User { Id = 1, Name = "John" });
var (status, user) = u; // deconstruction
// 3) Entities with count (paged)
var users = new List<User> { new() { Id = 1, Name = "John" } };
ResultEntities<User> list = Result.OkEntities(users, users.Count);
foreach (var it in list) { /* iterate directly */ }
// 4) Functional extensions
var dto = u.Map(x => new UserDto(x!.Id, x.Name));
var okOrThrow = u.Match(
onSuccess: () => dto,
onFailure: res => throw new InvalidOperationException(res.Status.ToString()));
// 5) Combine results
var combined = Result.Combine(Result.Ok(), Result.Error().AddError("E", "err"));
Core Components
1. Simple Result (Result)
Used for operations that don't return data, only a completion status and optional errors.
public Result DeleteUser(int id)
{
if (id <= 0)
return Result.ValidationError().AddError("id", "Invalid ID");
var deleted = _repository.Delete(id);
if (!deleted) return Result.NotExists();
return Result.Deleted();
}
// Deconstruction
var (status, errors) = DeleteUser(1);
if (status == StatusResult.Deleted) { /* ... */ }
2. Result with Entity (ResultEntity<T>)
Used for operations returning a single object.
public ResultEntity<User> GetUser(int id)
{
var user = _repository.Find(id);
if (user == null) return ResultEntity<User>.NotExists();
return ResultEntity<User>.Ok(user);
}
Implicit conversions and fluent API:
public ResultEntity<User> GetUser(int id)
{
var user = _repository.Find(id);
// T implicitly converts to ResultEntity<T>.Ok(entity)
return user ?? (ResultEntity<User>)StatusResult.NoExist;
}
// Deconstruction
var (status, user) = GetUser(1);
if (status == StatusResult.Ok) { /* ... */ }
// Fluent API
var result = ResultEntity<User>.Ok(user)
.WithCode(200)
.AddError("System", "Service throttled");
3. Result with Multiple Entities (ResultEntities<T>)
Used for lists or paged results. It includes an optional Count property for total records.
Direct iteration is supported via foreach as it implements IEnumerable<T>.
public ResultEntities<User> GetUsers(PagingParameters paging)
{
var (users, total) = _repository.GetAll(paging);
var result = ResultEntities<User>.Ok(users, total);
// Direct iteration is supported
foreach (var user in result)
{
// ...
}
return result;
}
4. Batch Result (ResultBatch<T>)
Used for processing multiple items in a single request, providing a global status and individual results for each item.
public ResultBatch<User> ImportUsers(List<User> users)
{
var results = users.Select(u => (ResultEntity<User>)Process(u)).ToList();
return ResultBatch<User>.Ok(results);
}
Error Handling
The library supports a fluent API for adding errors, which are stored in an ErrorCollection:
return Result.ValidationError()
.AddError("Email", "Invalid format")
.AddError("Password", "Too short")
.AddError(new Exception("Inner system error"));
Associate a numeric code with any result using WithCode():
return Result.Error().WithCode(4001);
Status Codes (StatusResult)
| Status | Category | Description |
|---|---|---|
Ok, Added, Updated, Deleted |
Success | Successful operations. |
NoExist |
Informational | Requested resource was not found. |
Warning |
Informational | Completed with non-critical issues. |
CancelOperation |
Control | Operation was cancelled. |
ValidationError |
Error | Client-side input validation failed. |
DataError, DatabaseError |
Error | Issues with data processing or persistence. |
ConcurrencyError |
Error | Data was modified by another process. |
ConnectionError |
Error | Connection to an external service failed. |
HttpError |
Error | An external HTTP call returned an error. |
Unauthorized |
Security | Authentication or authorization failed. |
ChangePassword |
Security | User must change their password. |
Exception |
Error | An unhandled exception occurred. |
Extensions
Import Pitasoft.Result.Extensions to use these helper methods on any IResult:
result.IsSuccess(): Returnstrueif status isOk,Added,Updated, orDeleted.result.IsError(): Returnstruefor most error-related statuses.result.IsFailure(): Returnstruefor any non-successful state, excludingNone,NoExist, andWarning. CoversUnauthorized,Exception,CancelOperation, and more.result.IsNotExist(): Returnstrueif the resource was not found.result.HasErrors(): Returnstrueif there are any errors in theErrorscollection.result.IsSuccessOrNotExist(): Useful for "Delete" operations where both cases are often handled similarly.
Functional API (Match and Map)
Import Pitasoft.Result.Extensions to use functional-style transformations:
Match — branch on success or failure
var dto = result.Match(
onSuccess: () => mapper.ToDto(result.Entity),
onFailure: r => throw new InvalidOperationException(r.Status.ToString())
);
Map — transform the entity if successful
ResultEntity<UserDto> dto = userResult.Map(user => mapper.ToDto(user));
Tap — execute an action without changing the result
result.Tap(() => _logger.LogInformation("Operation successful"))
.Tap(user => _cache.Set(user));
Bind — chain operations that return results (FlatMap)
ResultEntity<Order> result = GetUser(id)
.Bind(user => CreateOrder(user));
Ensure — validate a condition on the entity
ResultEntity<User> result = GetUser(id)
.Ensure(u => u.Age >= 18, "User must be an adult", "Age");
MapAsync — chain async operations
var result = await GetUserAsync(id)
.MapAsync(user => EnrichWithRolesAsync(user));
Pagination Parameters
Use PagingParameters to receive pagination input in your API endpoints:
public ResultEntities<User> GetUsers(PagingParameters paging)
{
// Index must be >= 0; Size must be > 0 (validated automatically)
var (users, total) = _repository.GetAll(paging);
return ResultEntities<User>.Ok(users, total);
}
Batch Helper (Batch<T>)
Use Batch<T> to describe a set of entities and the action to perform on them:
var batch = Batch<User>.Append(newUsers);
var batch = Batch<User>.Update(modifiedUsers);
var batch = Batch<User>.Delete(removedUsers);
Performance
Performance tips
- Prefer using the provided static factories (e.g.,
Result.Ok(),Result.Added()). They are very small and are annotated withAggressiveInliningto minimize overhead. ResultEntities<T>supports direct iteration viaforeach. Internally, its enumerator delegates to the underlyingEntitiescollection without usingyield, avoiding extra state-machine allocations.- When creating error results from exceptions, pass
deep: falseunless you explicitly need inner exception details. This reduces allocations in theErrorCollection. - For JSON payload size, consider ignoring nulls at the application level (e.g.,
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull). This reduces I/O without affecting CPU hot paths.
Benchmark results for core operations (Mean time / Allocated memory):
The library is optimized to minimize allocations and maximize throughput. Benchmark results for core operations (Mean time / Allocated memory):
| Operation | .NET 8 | .NET 9 | .NET 10 |
|---|---|---|---|
| Result.Ok | 17.0 ns / 64 B | 17.1 ns / 64 B | 17.5 ns / 64 B |
| ResultEntity.Ok | 17.7 ns / 72 B | 17.1 ns / 72 B | 18.3 ns / 72 B |
| ResultEntities.Ok | 17.2 ns / 80 B | 17.3 ns / 80 B | 17.7 ns / 80 B |
| Error (with Exception) | 43.2 ns / 408 B | 48.3 ns / 408 B | 37.9 ns / 408 B |
Note: Benchmarks performed on Apple M-series (Arm64).
Castellano
Pitasoft.Result es una librería .NET diseñada para estandarizar las respuestas de los servicios REST y las capas internas de la aplicación. Proporciona un conjunto robusto de clases para envolver datos, códigos de estado y colecciones de errores, facilitando un contrato de comunicación unificado entre APIs, servicios y clientes.
Características
- Respuestas Estandarizadas: Unifica las salidas de tu API usando
Result,ResultEntity<T>,ResultEntities<T>yResultBatch<T>. - Gestión de Errores Fluida: Añade fácilmente errores de validación, excepciones o violaciones de reglas de negocio mediante una interfaz fluida.
- Gestión de Estados Enriquecida: Enumerado
StatusResultintegrado que cubre escenarios comunes (Éxito, No Encontrado, Errores de Validación, Errores de Base de Datos, etc.). - Operaciones por Lote: Soporte especializado para procesar múltiples elementos con
ResultBatch<T>. - Soporte para Paginación:
ResultEntities<T>incluye metadatos como el conteo total, y la librería proporcionaPagingParameterspara el manejo de peticiones. - API Funcional: Métodos de extensión
MatchyMappara transformar resultados sin bloquesif/switch. - Soporte Async:
MatchAsyncyMapAsyncpara encadenar operaciones asíncronas de forma limpia. - Conversiones Implícitas: Convierte
StatusResulto entidades directamente a tipos de resultado sin código repetitivo. - Deconstrucción: Usa la deconstrucción de C# para extraer valores y estado fácilmente:
var (status, user) = result;. - Extensiones: Métodos de ayuda útiles para comprobar éxito, errores específicos o estado.
- API Funcional:
Match,Map,Tap,BindyEnsurepara el procesamiento fluido de resultados. - Combinar Resultados: Agrega múltiples resultados en uno solo con
Result.Combine(). - Rendimiento: Implementación de alto rendimiento con mínimas asignaciones y
AggressiveInlining.
Instalación
dotnet add package Pitasoft.Result
Inicio rápido
// 1) Resultado simple
Result r1 = Result.Ok();
Result r2 = Result.ValidationError().AddError("Email", "No válido");
// 2) Entidad
ResultEntity<User> u = Result.Ok(new User { Id = 1, Name = "John" });
var (status, user) = u; // deconstrucción
// 3) Colección con total (paginado)
var users = new List<User> { new() { Id = 1, Name = "John" } };
ResultEntities<User> list = Result.OkEntities(users, users.Count);
foreach (var it in list) { /* iteración directa */ }
// 4) Extensiones funcionales
var dto = u.Map(x => new UserDto(x!.Id, x.Name));
var okOTira = u.Match(
onSuccess: () => dto,
onFailure: res => throw new InvalidOperationException(res.Status.ToString()));
// 5) Combinar resultados
var combinado = Result.Combine(Result.Ok(), Result.Error().AddError("E", "err"));
Componentes Principales
1. Resultado Simple (Result)
Utilizado para operaciones que no devuelven datos, solo un estado de finalización y errores opcionales.
public Result DeleteUser(int id)
{
if (id <= 0)
return Result.ValidationError().AddError("id", "ID no válido");
var deleted = _repository.Delete(id);
if (!deleted) return Result.NotExists();
return Result.Deleted();
}
// Deconstrucción
var (status, errors) = DeleteUser(1);
if (status == StatusResult.Deleted) { /* ... */ }
2. Resultado con Entidad (ResultEntity<T>)
Utilizado para operaciones que devuelven un único objeto.
public ResultEntity<User> GetUser(int id)
{
var user = _repository.Find(id);
if (user == null) return ResultEntity<User>.NotExists();
return ResultEntity<User>.Ok(user);
}
Conversiones implícitas y API fluida:
public ResultEntity<User> GetUser(int id)
{
var user = _repository.Find(id);
// T se convierte implícitamente a ResultEntity<T>.Ok(entity)
return user ?? (ResultEntity<User>)StatusResult.NoExist;
}
// Deconstrucción
var (status, user) = GetUser(1);
if (status == StatusResult.Ok) { /* ... */ }
// API Fluida
var result = ResultEntity<User>.Ok(user)
.WithCode(200)
.AddError("System", "Servicio saturado");
3. Resultado con Múltiples Entidades (ResultEntities<T>)
Utilizado para listas o resultados paginados. Incluye una propiedad opcional Count para el total de registros.
Se puede iterar directamente sobre el objeto mediante foreach, ya que implementa IEnumerable<T>.
public ResultEntities<User> GetUsers(PagingParameters paging)
{
var (users, total) = _repository.GetAll(paging);
var result = ResultEntities<User>.Ok(users, total);
// Se puede iterar directamente
foreach (var user in result)
{
// ...
}
return result;
}
4. Resultado por Lote (ResultBatch<T>)
Utilizado para procesar múltiples elementos en una sola petición, proporcionando un estado global y resultados individuales para cada elemento.
public ResultBatch<User> ImportUsers(List<User> users)
{
var results = users.Select(u => (ResultEntity<User>)Process(u)).ToList();
return ResultBatch<User>.Ok(results);
}
Gestión de Errores
La librería soporta una API fluida para añadir errores, que se almacenan en una ErrorCollection:
return Result.ValidationError()
.AddError("Email", "Formato no válido")
.AddError("Password", "Demasiado corta")
.AddError(new Exception("Error interno del sistema"));
Asocia un código numérico a cualquier resultado con WithCode():
return Result.Error().WithCode(4001);
Códigos de Estado (StatusResult)
| Estado | Categoría | Descripción |
|---|---|---|
Ok, Added, Updated, Deleted |
Éxito | Operaciones exitosas. |
NoExist |
Informativo | El recurso solicitado no fue encontrado. |
Warning |
Informativo | Completado con problemas no críticos. |
CancelOperation |
Control | La operación fue cancelada. |
ValidationError |
Error | Falló la validación de entrada del cliente. |
DataError, DatabaseError |
Error | Problemas en el procesamiento o persistencia de datos. |
ConcurrencyError |
Error | Los datos fueron modificados por otro proceso. |
ConnectionError |
Error | Falló la conexión a un servicio externo. |
HttpError |
Error | Una llamada HTTP externa devolvió un error. |
Unauthorized |
Seguridad | Falló la autenticación o autorización. |
ChangePassword |
Seguridad | El usuario debe cambiar su contraseña. |
Exception |
Error | Ocurrió una excepción no controlada. |
Extensiones
Importa Pitasoft.Result.Extensions para usar estos métodos de ayuda en cualquier IResult:
result.IsSuccess(): Devuelvetruesi el estado esOk,Added,UpdatedoDeleted.result.IsError(): Devuelvetruepara la mayoría de los estados relacionados con errores.result.IsFailure(): Devuelvetruepara cualquier estado no exitoso, excluyendoNone,NoExistyWarning. CubreUnauthorized,Exception,CancelOperationy más.result.IsNotExist(): Devuelvetruesi el recurso no fue encontrado.result.HasErrors(): Devuelvetruesi hay algún error en la colecciónErrors.result.IsSuccessOrNotExist(): Útil para operaciones "Delete" donde ambos casos suelen manejarse de forma similar.
API Funcional (Match y Map)
Importa Pitasoft.Result.Extensions para usar transformaciones de estilo funcional:
Match — bifurcar según éxito o error
var dto = result.Match(
onSuccess: () => mapper.ToDto(result.Entity),
onFailure: r => throw new InvalidOperationException(r.Status.ToString())
);
Map — transformar la entidad si el resultado es exitoso
ResultEntity<UserDto> dto = userResult.Map(user => mapper.ToDto(user));
Tap — ejecutar una acción sin cambiar el resultado
result.Tap(() => _logger.LogInformation("Operación exitosa"))
.Tap(user => _cache.Set(user));
Bind — encadenar operaciones que devuelven resultados (FlatMap)
ResultEntity<Order> result = GetUser(id)
.Bind(user => CreateOrder(user));
Ensure — validar una condición sobre la entidad
ResultEntity<User> result = GetUser(id)
.Ensure(u => u.Age >= 18, "El usuario debe ser mayor de edad", "Edad");
MapAsync — encadenar operaciones asíncronas
var result = await GetUserAsync(id)
.MapAsync(user => EnrichWithRolesAsync(user));
Parámetros de Paginación
Usa PagingParameters para recibir la entrada de paginación en tus endpoints:
public ResultEntities<User> GetUsers(PagingParameters paging)
{
// Index debe ser >= 0; Size debe ser > 0 (validado automáticamente)
var (users, total) = _repository.GetAll(paging);
return ResultEntities<User>.Ok(users, total);
}
Helper de Lote (Batch<T>)
Usa Batch<T> para describir un conjunto de entidades y la acción a realizar sobre ellas:
var batch = Batch<User>.Append(newUsers);
var batch = Batch<User>.Update(modifiedUsers);
var batch = Batch<User>.Delete(removedUsers);
Rendimiento
Consejos de rendimiento
- Prefiere las factorías estáticas provistas (por ejemplo,
Result.Ok(),Result.Added()). Son muy pequeñas y están anotadas conAggressiveInliningpara minimizar el overhead. ResultEntities<T>permite la iteración directa conforeach. Internamente, su enumerador delega en la colecciónEntitiessin usaryield, evitando asignaciones de la máquina de estados.- Al crear resultados de error desde excepciones, usa
deep: falsesalvo que necesites explícitamente el detalle de inner exceptions. Esto reduce asignaciones enErrorCollection. - Para reducir el tamaño de los JSON, valora ignorar nulos a nivel de aplicación (por ejemplo,
JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull). Esto reduce E/S sin afectar a rutas críticas de CPU.
Resultados de benchmarks para operaciones principales (Tiempo medio / Memoria asignada):
La librería está optimizada para minimizar asignaciones y maximizar el rendimiento. Resultados de benchmarks para operaciones principales (Tiempo medio / Memoria asignada):
| Operación | .NET 8 | .NET 9 | .NET 10 |
|---|---|---|---|
| Result.Ok | 17.0 ns / 64 B | 17.1 ns / 64 B | 17.5 ns / 64 B |
| ResultEntity.Ok | 17.7 ns / 72 B | 17.1 ns / 72 B | 18.3 ns / 72 B |
| ResultEntities.Ok | 17.2 ns / 80 B | 17.3 ns / 80 B | 17.7 ns / 80 B |
| Error (con Excepción) | 43.2 ns / 408 B | 48.3 ns / 408 B | 37.9 ns / 408 B |
Nota: Benchmarks realizados en Apple M-series (Arm64).
Autor
Sebastián Martínez Pérez
License
Copyright © 2019-2026 Pitasoft, S.L. Licensed under the LICENSE.txt provided in this repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Pitasoft.Error (>= 5.3.1)
-
net8.0
- Pitasoft.Error (>= 5.3.1)
-
net9.0
- Pitasoft.Error (>= 5.3.1)
NuGet packages (9)
Showing the top 5 NuGet packages that depend on Pitasoft.Result:
| Package | Downloads |
|---|---|
|
Pitasoft.Client
.NET library designed to simplify the consumption of RESTful services. It provides a robust base class and helpers to handle HTTP requests, JSON serialization, and common API patterns. |
|
|
Pitasoft.Web
Librerias basicas de aplicaciones web |
|
|
Pitasoft.Blazor.Result
Application of the functionalities of the Pitasoft.Blazor package, adding functionalities of Pitasoft.Result. |
|
|
Pitasoft.Mail
E-Mail server service. |
|
|
Pitasoft.Result.AspNetCore
ASP.NET Core integration for Pitasoft.Result. Provides HTTP result mapping, JSON helpers, DI-based configuration, and exception handling for MVC and Minimal APIs. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 7.4.1 | 159 | 4/19/2026 |
| 7.3.2 | 220 | 4/3/2026 |
| 7.3.1 | 359 | 3/26/2026 |
| 7.2.9 | 162 | 3/24/2026 |
| 7.2.8 | 140 | 3/23/2026 |
| 7.2.7 | 142 | 3/22/2026 |
| 7.2.6 | 172 | 3/20/2026 |
| 7.2.5 | 144 | 3/20/2026 |
| 7.2.4 | 208 | 3/13/2026 |
| 7.2.3 | 145 | 3/11/2026 |
| 7.2.2 | 166 | 3/10/2026 |
| 7.2.1 | 157 | 3/9/2026 |
| 7.1.4 | 196 | 3/2/2026 |
| 7.1.3 | 184 | 2/24/2026 |
| 7.1.2 | 150 | 2/24/2026 |
| 7.1.1 | 191 | 2/23/2026 |
| 7.0.2 | 176 | 2/12/2026 |
| 7.0.1 | 203 | 1/26/2026 |
| 6.5.7 | 393 | 5/27/2025 |
| 6.5.6 | 361 | 5/27/2025 |