Pitasoft.Result 7.2.7

There is a newer version of this package available.
See the version list below for details.
dotnet add package Pitasoft.Result --version 7.2.7
                    
NuGet\Install-Package Pitasoft.Result -Version 7.2.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Pitasoft.Result" Version="7.2.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Pitasoft.Result" Version="7.2.7" />
                    
Directory.Packages.props
<PackageReference Include="Pitasoft.Result" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Pitasoft.Result --version 7.2.7
                    
#r "nuget: Pitasoft.Result, 7.2.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Pitasoft.Result@7.2.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Pitasoft.Result&version=7.2.7
                    
Install as a Cake Addin
#tool nuget:?package=Pitasoft.Result&version=7.2.7
                    
Install as a Cake Tool

Pitasoft.Result

Build Status NuGet NuGet Downloads License Target Framework

English | Castellano


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>, ResultPaged<T>, and ResultBatch<T>.
  • Automatic Timestamps: All results now include a CalculationTime (DateTimeOffset?) to track when they were generated, via the IHasCalculationTimestamp interface.
  • Fluent Error Handling: Easily add validation errors, exceptions, or business rule violations using a fluent interface. Includes Result.Try and Result.TryAsync for automatic exception handling, with built-in support for CancellationToken and OperationCanceledExceptionCancelOperation.
  • Rich Status Management: Built-in StatusResult enum covering common API scenarios (Success, Not Found, Forbidden, Conflict, Validation Errors, Database Errors, etc.). Includes StatusResultExtensions for semantic categorization (IsSuccess, IsInfrastructureError, IsBusinessError, IsTransientError, IsError).
  • Batch Operations: Specialized support for processing multiple items with ResultBatch<T> using IReadOnlyList<T> for efficient access.
  • Pagination Support: ResultPaged<T> provides full paging metadata (TotalCount, Page, PageSize, TotalPages, etc.). Includes a full set of static factory methods in the Result class (e.g., Result.ErrorPaged, Result.DatabaseErrorPaged, Result.NotFoundPaged) and TryPagedAsync for exception-safe paged queries.
  • Async Support: Native support for IAsyncEnumerable<T> and Task<T> with MaterializeAsync and ToResultEntitiesAsync. Includes MapAsync, BindAsync, MatchAsync, and TapAsync extensions.
  • Improved Serialization: Custom JSON converters for ResultEntities<T> and ResultPaged<T> ensure clean and predictable API outputs.
  • Implicit Conversions: Convert StatusResult or entities directly to result types with zero boilerplate.
  • Deconstruction: Use C# deconstruction to extract values and status easily: var (status, user) = result;.
  • IsSuccess / IsFailure Properties: Available directly on every result instance (result.IsSuccess, result.IsFailure) without needing extension methods.
  • Extensions: Useful helper methods to check for success, specific errors, or state. Including ToOkResult(), ToAddedResult(), ToUpdatedResult(), and ToDeletedResult() for converting entities to results.
  • Functional API: Match, MatchAsync, Map, MapAsync, Tap, TapAsync, Bind, and Ensure for fluent result processing. All operations are available for Result, ResultEntity<T>, ResultEntities<T>, and ResultPaged<T>.
  • Combine Results: Aggregate multiple results into one with Result.Combine() and Result.CombineAsync().
  • Multi-value Parameters: IParameters.GetParameters() returns IEnumerable<KeyValuePair<string, string>>, supporting multiple values for the same key (e.g., ?tag=a&tag=b).
  • ErrorCollection Improvements: Empty is now a property (safe, non-shared instance). Direct Count property for O(1) access.
  • Performance: High-performance implementation with minimal allocations, AggressiveInlining, and efficient collection materialization using IReadOnlyList<T>.

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
var (s, e) = u; // status and entity
var (st, code, errs) = (Result)u; // deconstruction with result code from base Result

// 3) Paged entities
var users = new List<User> { new() { Id = 1, Name = "John" } };
ResultPaged<User> paged = Result.OkPaged(users, totalCount: 100, page: 1, pageSize: 10);
foreach (var it in paged) { /* iterate directly */ }

// 4) Try/Catch automation (with CancellationToken support)
var result = Result.Try(() => DoWork());
var resultAsync = await Result.TryAsync(async ct => await DoWorkAsync(ct), cancellationToken);

// 5) Try variants for collections
ResultEntities<User> entities = Result.TryEntities(() => GetUsers());
ResultEntities<User> entitiesAsync = await Result.TryEntitiesAsync(async () => await GetUsersAsync());
ResultPaged<User> pagedResult = await Result.TryPagedAsync(
    async () => await GetPagedAsync(), page: 1, pageSize: 20);

// 6) Functional extensions (available on all result types)
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()));

// MatchAsync on ResultEntities / ResultPaged
var response = await pagedResult.MatchAsync(
    onSuccess: async p => await BuildPagedDto(p),
    onFailure: async r => await HandleError(r));

// 7) Combine results (sync and async)
var combined = Result.Combine(Result.Ok(), Result.Error().AddError("E", "err"));
var combinedAsync = await Result.CombineAsync(Task1Async(), Task2Async());

// 8) IsSuccess / IsFailure as properties
if (result.IsSuccess) { /* ... */ }
if (result.IsFailure) { /* ... */ }

// 9) StatusResult semantic extensions
if (result.Status.IsInfrastructureError()) { /* retry logic */ }
if (result.Status.IsBusinessError()) { /* return 422 */ }

// 10) Calculation Timestamp
DateTimeOffset? time = result.CalculationTime;

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 of results. It uses IReadOnlyList<T> for the Entities property to ensure efficiency. Direct iteration is supported via foreach as it implements IEnumerable<T>.

public ResultEntities<User> GetActiveUsers()
{
    IEnumerable<User> users = _repository.GetActive();
    // Materializes the collection into IReadOnlyList
    return Result.OkEntities(users);
}

Use TryEntities / TryEntitiesAsync to handle exceptions automatically:

public Task<ResultEntities<User>> GetActiveUsersAsync()
    => Result.TryEntitiesAsync(() => _repository.GetActiveAsync());
4. Paged Result (ResultPaged<T>)

Inherits from ResultEntities<T> and includes complete metadata for paginated results (TotalCount, Page, PageSize, TotalPages, HasNextPage, HasPreviousPage).

Create paged results easily using the Result static class:

public ResultPaged<User> GetUsers(PagingParameters paging)
{
    try 
    {
        var (users, total) = _repository.GetAll(paging);
        return Result.OkPaged(users, total, paging.Page, paging.PageSize);
    }
    catch (Exception ex)
    {
        return Result.DatabaseErrorPaged<User>(ex);
    }
}

// Deconstruction
var (status, users, total, page, pageSize) = result;

Use TryPagedAsync to handle exceptions and cancellation automatically:

public Task<ResultPaged<User>> GetUsersAsync(PagingParameters paging, CancellationToken ct)
    => Result.TryPagedAsync(
        async () => await _repository.GetAllAsync(paging, ct),
        paging.Page, paging.PageSize);
5. Batch Result (ResultBatch<T>)

Used for processing multiple items in a single request, providing a global status and individual results for each item using IReadOnlyList<ResultEntity<T>>.

public ResultBatch<User> ImportUsers(List<User> users)
{
    var results = users.Select(u => (ResultEntity<User>)Process(u));
    return ResultBatch<User>.Ok(results);
}

// Deconstruction
var (status, results) = importResult;

Static factory methods are available for all common statuses:

  • ResultBatch<T>.Ok(entities)
  • ResultBatch<T>.ValidationError(errors)
  • ResultBatch<T>.DatabaseError(ex)
  • ResultBatch<T>.NotFound()
  • ResultBatch<T>.Forbidden()
  • ... and all other StatusResult states.

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);

ErrorCollection exposes a Count property for O(1) access and Empty is always a fresh, non-shared instance:

bool hasErrors = result.Errors.Count > 0;
var emptyErrors = ErrorCollection.Empty; // always returns a new instance

Status Codes (StatusResult)

Status Category Description
Ok, Added, Updated, Deleted Success Successful operations.
NoExist Informational Requested resource was not found (semantic domain).
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.
Forbidden Security The server understood the request but refuses to authorize it (403).
NotFound Error The requested resource was not found (404).
Conflict Error The request conflicts with the current state of the server (409).
UnprocessableEntity Error Semantic errors in the request (422).
TooManyRequests Error Rate limit exceeded (429).
ServiceUnavailable Error Server is not ready to handle the request (503).
ChangePassword Security User must change their password.
Error, Exception Error Generic error or an unhandled exception.

StatusResult Extensions

Import Pitasoft.Result.Extensions to use semantic classification on StatusResult values:

if (result.Status.IsSuccess())            { /* Ok / Added / Updated / Deleted */ }
if (result.Status.IsInfrastructureError()) { /* DatabaseError / ConcurrencyError / ConnectionError / HttpError / ServiceUnavailable */ }
if (result.Status.IsBusinessError())       { /* ValidationError / DataError / NoExist / Unauthorized / Forbidden / NotFound / Conflict / UnprocessableEntity / ChangePassword */ }
if (result.Status.IsTransientError())      { /* Error / Exception / CancelOperation / ConcurrencyError / ConnectionError / HttpError / TooManyRequests / ServiceUnavailable */ }
if (result.Status.IsError())               { /* General or specialized error states (Validation, Data, Database, Concurrency, HTTP, Exception, etc.) */ }

These are especially useful in middleware or retry policies to decide how to handle a failure without inspecting the Status value directly.

Extensions

Import Pitasoft.Result.Extensions to use these helper methods on any IResult:

  • result.IsSuccess / result.IsFailure: Properties available directly on every result instance — no extension call needed.
  • result.IsSuccess(): Returns true if status is Ok, Added, Updated, or Deleted.
  • result.IsError(): Returns true for most error-related statuses.
  • result.IsFailure(): Returns true for any non-successful state, excluding None, NoExist, and Warning. Covers Unauthorized, Exception, CancelOperation, and more.
  • result.IsOk(): Returns true if the status is exactly Ok.
  • result.IsWarning(): Returns true if the status is Warning.
  • result.IsNotExist(): Returns true if the resource was not found (NoExist).
  • result.IsUnauthorized(): Returns true if the status is Unauthorized.
  • result.IsForbidden() / result.IsNotFound() / result.IsConflict() / result.IsTooManyRequests() / result.IsUnprocessableEntity() / result.IsServiceUnavailable(): Semantic checks for HTTP-like error states.
  • result.HasErrors(): Returns true if there are any errors in the Errors collection.
  • result.IsSuccessOrNotExist(): Useful for "Delete" operations where both cases are often handled similarly.
  • result.IsSuccessOrWarning(): Returns true for successful or warning states.
  • Materialize() / MaterializeAsync(): Convert IEnumerable<T> or IAsyncEnumerable<T> to ResultEntities<T> or ResultPaged<T>.
  • UpdateTimestamp(): Manually updates the CalculationTime to the current UTC time.
  • ToPaged(): Convert a result to a ResultPaged<T> preserving state.
  • ToResultEntity() / ToResultEntities(): Convert between result types preserving metadata.
  • ToOkResult() / ToAddedResult() / ToUpdatedResult() / ToDeletedResult(): Convert an entity to a ResultEntity<T> with the corresponding success status.

Functional API (Match, Map, Bind, Ensure, EnsureAsync, Recover, Tap, TapAsync)

Import Pitasoft.Result.Extensions to use functional-style transformations.

Match — branch on success or failure

Available for Result, ResultEntity<T>, ResultEntities<T>, and ResultPaged<T>:

// Result
string msg = result.Match(
    onSuccess: () => "Done",
    onFailure: r => $"Error: {r.Status}");

// ResultEntity<T>
var dto = userResult.Match(
    onSuccess: () => mapper.ToDto(userResult.Entity),
    onFailure: r => throw new InvalidOperationException(r.Status.ToString()));

// ResultEntities<T> / ResultPaged<T>
var view = pagedResult.Match(
    onSuccess: () => BuildView(pagedResult),
    onFailure: r => ErrorView(r));
MatchAsync — async branch on success or failure

Available for Result, ResultEntity<T>, ResultEntities<T>, and ResultPaged<T>:

var response = await pagedResult.MatchAsync(
    onSuccess: async p => await BuildPagedResponseAsync(p),
    onFailure: async r => await HandleFailureAsync(r));
Map — transform the entity if successful

Available for ResultEntity<T>, ResultEntities<T>, and ResultPaged<T>:

ResultEntity<UserDto>    dto    = userResult.Map(user => mapper.ToDto(user));
ResultEntities<UserDto>  dtos   = usersResult.Map(user => mapper.ToDto(user));
ResultPaged<UserDto>     paged  = pagedResult.Map(user => mapper.ToDto(user));
MapAsync — chain async transformations
ResultEntity<UserDto>   dto   = await userResult.MapAsync(async u => await EnrichAsync(u));
ResultEntities<UserDto> dtos  = await usersResult.MapAsync(async u => await EnrichAsync(u));
ResultPaged<UserDto>    paged = await pagedResult.MapAsync(async u => await EnrichAsync(u));
Tap — execute an action without changing the result
result.Tap(() => _logger.LogInformation("Operation successful"))
      .Tap(user => _cache.Set(user));
TapAsync — async side-effect without changing the result

Works on any result type (Result, ResultEntity<T>, ResultEntities<T>, ResultPaged<T>):

var result = await GetUserAsync(id)
    .TapAsync(async () => await _auditService.LogAsync("user fetched"));

// Typed variant
var result = await GetUserAsync(id)
    .TapAsync(async user => await _cache.SetAsync(user));
Bind — chain operations that return results (FlatMap)

Available for ResultEntity<T>, ResultEntities<T>, and ResultPaged<T>:

ResultEntity<Order> result = GetUser(id)
    .Bind(user => CreateOrder(user));

ResultPaged<OrderDto> paged = GetPagedOrders(page, size)
    .Bind(o => EnrichOrder(o));
Ensure — validate a condition

On Result (base):

Result result = CheckPermissions(userId)
    .Ensure(() => _quota.HasCapacity(), "Quota exceeded", "Quota");

On ResultEntity<T>:

ResultEntity<User> result = GetUser(id)
    .Ensure(u => u.Age >= 18, "User must be an adult", "Age");
EnsureAsync — validate an asynchronous condition

Perform validations that require I/O, like database checks, within the fluent chain:

ResultEntity<User> result = await GetUser(id)
    .EnsureAsync(async u => await _repo.IsEmailUniqueAsync(u.Email), "Email already exists", "Email");
Recover — provide a fallback value on failure

Safely handle failures by providing a default value or a recovery function:

// Simple fallback
User user = GetUser(id).Recover(new User { Name = "Guest" });

// Recovery function
User user = GetUser(id).Recover(r => new User { Name = $"Guest (Error: {r.Status})" });

Exception-safe Factory Methods (Try)

All Try variants automatically catch exceptions. OperationCanceledException is mapped to CancelOperation instead of Exception. You can now optionally provide a mapException function to transform specific exceptions into meaningful StatusResult codes.

Method Returns Notes
Result.Try(action, deep?, mapEx?) Result Sync, no return value
Result.Try<T>(func, deep?, mapEx?) ResultEntity<T> Sync, returns entity
Result.TryAsync(action, ct?, deep?, mapEx?) Task<Result> Async, supports CancellationToken
Result.TryAsync<T>(func, ct?, deep?, mapEx?) Task<ResultEntity<T>> Async entity, supports CancellationToken
Result.TryEntities<T>(func, deep?, mapEx?) ResultEntities<T> Sync collection
Result.TryEntitiesAsync<T>(func, deep?, mapEx?) Task<ResultEntities<T>> Async collection
Result.TryPagedAsync<T>(func, page, pageSize, deep?, mapEx?) Task<ResultPaged<T>> Async paged collection
// Automatic mapping of exceptions to status codes
var result = Result.Try(() => _repo.Get(id), 
    mapException: ex => ex is KeyNotFoundException ? StatusResult.NotFound : StatusResult.Error);

// CancellationToken support — OperationCanceledException → CancelOperation
var result = await Result.TryAsync(
    async ct => await _service.ProcessAsync(ct),
    cancellationToken);

// Entities
ResultEntities<Product> products = Result.TryEntities(() => _repo.GetAll());

// Paged
ResultPaged<Product> page = await Result.TryPagedAsync(
    async () => await _repo.GetPagedAsync(1, 20),
    page: 1, pageSize: 20);

Combine Results

Aggregate multiple Result instances into one. The combined result is Ok if all succeed; otherwise it carries all errors from failed results.

// Synchronous
Result combined = Result.Combine(
    ValidateName(dto.Name),
    ValidateEmail(dto.Email),
    ValidateAge(dto.Age));

// Asynchronous
Result combined = await Result.CombineAsync(
    ValidateNameAsync(dto.Name),
    ValidateEmailAsync(dto.Email));

Pagination and Entity Parameters

Use PagingParameters to receive pagination input and EntityParameters for filtering, searching, and sorting.

GetParameters() returns IEnumerable<KeyValuePair<string, string>>, which supports multiple values for the same key (useful for array-style query strings like ?tag=a&tag=b):

public ResultPaged<User> GetUsers(EntityParameters parameters)
{
    // parameters.Page, parameters.PageSize, parameters.Search, parameters.Query, parameters.Order, parameters.Attrs
    var (users, total) = _repository.GetAll(parameters);
    return Result.OkPaged(users, total, parameters.Page, parameters.PageSize);
}

// Convert to Dictionary when a single-value map is sufficient
var dict = parameters.GetParameters().ToDictionary(kv => kv.Key, kv => kv.Value);

// Or use directly with HttpClient query builders that accept IEnumerable<KVP>
var query = QueryString.Create(parameters.GetParameters());

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
  • Instance Cache (Singleton): Common Result states (NotExists(), ValidationError()) now use reusable static instances. This reduces allocation cost from $O(n)$ to $O(1)$ for these frequent calls.
  • Parameter Optimization: PagingParameters.GetParameters() and EntityParameters.GetParameters() now use yield return instead of creating temporary lists, improving memory efficiency from $O(n)$ to $O(1)$ space.
  • Collection Efficiency: ResultEntities<T> and ResultPaged<T> use IReadOnlyList<T> for the Entities property. Collections are materialized efficiently into a list only when necessary.
  • Aggressive Inlining: Static factories and extension methods are annotated with AggressiveInlining to minimize overhead.
  • Direct Iteration: Supported via foreach. Internally, its enumerator delegates to the underlying collection without using yield, avoiding extra state-machine allocations.
  • JSON Payload: ResultPaged<T> automatically ignores calculated properties like TotalPages, HasNextPage, and HasPreviousPage during serialization to reduce payload size.
  • IsSuccess / IsFailure Properties: Use these properties instead of extension method calls in hot paths — they are simple property reads with no method dispatch overhead.
  • Exception handling: When creating error results from exceptions manually, pass deep: false unless you explicitly need inner exception details to reduce allocations in ErrorCollection.

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>, ResultPaged<T> y ResultBatch<T>.
  • Marcas de Tiempo Automáticas: Todos los resultados incluyen ahora CalculationTime (DateTimeOffset?) para rastrear cuándo fueron generados, a través de la interfaz IHasCalculationTimestamp.
  • Gestión de Errores Fluida: Añade fácilmente errores de validación, excepciones o violaciones de reglas de negocio mediante una interfaz fluida. Incluye Result.Try y Result.TryAsync con soporte nativo para CancellationToken y conversión automática de OperationCanceledExceptionCancelOperation.
  • Gestión de Estados Enriquecida: Enumerado StatusResult integrado que cubre escenarios comunes (Éxito, No Encontrado, Prohibido, Conflicto, Errores de Validación, Errores de Base de Datos, etc.). Incluye StatusResultExtensions para clasificación semántica (IsSuccess, IsInfrastructureError, IsBusinessError, IsTransientError, IsError).
  • Operaciones por Lote: Soporte especializado para procesar múltiples elementos con ResultBatch<T> usando IReadOnlyList<T> para un acceso eficiente.
  • Soporte para Paginación: ResultPaged<T> proporciona metadatos completos de paginación (TotalCount, Page, PageSize, TotalPages, etc.). Incluye un conjunto completo de métodos de factoría estáticos en la clase Result (ej., Result.ErrorPaged, Result.DatabaseErrorPaged, Result.NotFoundPaged) y TryPagedAsync para consultas paginadas con manejo automático de excepciones.
  • Soporte Async: Soporte nativo para IAsyncEnumerable<T> y Task<T> con MaterializeAsync y ToResultEntitiesAsync. Incluye extensiones MapAsync, BindAsync, MatchAsync y TapAsync.
  • Serialización Mejorada: Conversores JSON personalizados para ResultEntities<T> y ResultPaged<T> garantizan salidas de API limpias y predecibles.
  • Conversiones Implícitas: Convierte StatusResult o 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;.
  • Propiedades IsSuccess / IsFailure: Disponibles directamente en cada instancia de resultado (result.IsSuccess, result.IsFailure) sin necesidad de métodos de extensión.
  • Extensiones: Métodos de ayuda útiles para comprobar éxito, errores específicos o estado. Incluyendo ToOkResult(), ToAddedResult(), ToUpdatedResult() y ToDeletedResult() para convertir entidades en resultados.
  • API Funcional: Match, MatchAsync, Map, MapAsync, Tap, TapAsync, Bind y Ensure para el procesamiento fluido de resultados. Todas las operaciones están disponibles para Result, ResultEntity<T>, ResultEntities<T> y ResultPaged<T>.
  • Combinar Resultados: Agrega múltiples resultados en uno solo con Result.Combine() y Result.CombineAsync().
  • Parámetros Multivalor: IParameters.GetParameters() devuelve IEnumerable<KeyValuePair<string, string>>, soportando múltiples valores para la misma clave (ej., ?tag=a&tag=b).
  • Mejoras en ErrorCollection: Empty es ahora una propiedad (instancia nueva y segura). Propiedad Count directa con acceso O(1).
  • Rendimiento: Implementación de alto rendimiento con mínimas asignaciones, AggressiveInlining y materialización eficiente de colecciones mediante IReadOnlyList<T>.

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
var (s, e) = u; // estado y entidad
var (st, code, errs) = (Result)u; // deconstrucción con código de resultado desde Result base

// 3) Colección paginada
var users = new List<User> { new() { Id = 1, Name = "John" } };
ResultPaged<User> paginado = Result.OkPaged(users, totalCount: 100, page: 1, pageSize: 10);
foreach (var it in paginado) { /* iteración directa */ }

// 4) Automatización Try/Catch (con soporte CancellationToken)
var resultado = Result.Try(() => HacerTrabajo());
var resultadoAsync = await Result.TryAsync(async ct => await HacerTrabajoAsync(ct), cancellationToken);

// 5) Variantes Try para colecciones
ResultEntities<User> entidades = Result.TryEntities(() => ObtenerUsuarios());
ResultEntities<User> entidadesAsync = await Result.TryEntitiesAsync(async () => await ObtenerUsuariosAsync());
ResultPaged<User> resultadoPaginado = await Result.TryPagedAsync(
    async () => await ObtenerPaginadoAsync(), page: 1, pageSize: 20);

// 6) Extensiones funcionales (disponibles en todos los tipos de resultado)
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()));

// MatchAsync en ResultEntities / ResultPaged
var respuesta = await resultadoPaginado.MatchAsync(
    onSuccess: async p => await ConstruirDtoPaginado(p),
    onFailure: async r => await ManejarError(r));

// 7) Combinar resultados (síncrono y asíncrono)
var combinado = Result.Combine(Result.Ok(), Result.Error().AddError("E", "err"));
var combinadoAsync = await Result.CombineAsync(Tarea1Async(), Tarea2Async());

// 8) IsSuccess / IsFailure como propiedades
if (resultado.IsSuccess) { /* ... */ }
if (resultado.IsFailure) { /* ... */ }

// 9) Extensiones semánticas de StatusResult
if (resultado.Status.IsInfrastructureError()) { /* lógica de reintento */ }
if (resultado.Status.IsBusinessError()) { /* devolver 422 */ }

// 10) Marca de tiempo de cálculo
DateTimeOffset? tiempo = resultado.CalculationTime;

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 de resultados. Utiliza IReadOnlyList<T> para la propiedad Entities para garantizar la eficiencia. Se puede iterar directamente sobre el objeto mediante foreach, ya que implementa IEnumerable<T>.

public ResultEntities<User> GetActiveUsers()
{
    IEnumerable<User> users = _repository.GetActive();
    // Materializa la colección en IReadOnlyList
    return Result.OkEntities(users);
}

Usa TryEntities / TryEntitiesAsync para manejar excepciones automáticamente:

public Task<ResultEntities<User>> GetActiveUsersAsync()
    => Result.TryEntitiesAsync(() => _repository.GetActiveAsync());
4. Resultado Paginado (ResultPaged<T>)

Hereda de ResultEntities<T> e incluye metadatos completos para resultados paginados (TotalCount, Page, PageSize, TotalPages, HasNextPage, HasPreviousPage).

Crea resultados paginados fácilmente usando la clase estática Result:

public ResultPaged<User> GetUsers(PagingParameters paging)
{
    try
    {
        var (users, total) = _repository.GetAll(paging);
        return Result.OkPaged(users, total, paging.Page, paging.PageSize);
    }
    catch (Exception ex)
    {
        return Result.DatabaseErrorPaged<User>(ex);
    }
}

// Deconstrucción
var (status, users, total, pagina, tamañoPagina) = result;

Usa TryPagedAsync para manejar excepciones y cancelación automáticamente:

public Task<ResultPaged<User>> GetUsersAsync(PagingParameters paging, CancellationToken ct)
    => Result.TryPagedAsync(
        async () => await _repository.GetAllAsync(paging, ct),
        paging.Page, paging.PageSize);
5. 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 usando IReadOnlyList<ResultEntity<T>>.

public ResultBatch<User> ImportUsers(List<User> users)
{
    var results = users.Select(u => (ResultEntity<User>)Process(u));
    return ResultBatch<User>.Ok(results);
}

// Deconstrucción
var (status, results) = importResult;

Los métodos de factoría estáticos están disponibles para todos los estados comunes:

  • ResultBatch<T>.Ok(entities)
  • ResultBatch<T>.ValidationError(errors)
  • ResultBatch<T>.DatabaseError(ex)
  • ResultBatch<T>.NotFound()
  • ResultBatch<T>.Forbidden()
  • ... y todos los demás estados de StatusResult.

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);

ErrorCollection expone una propiedad Count para acceso O(1) y Empty siempre devuelve una instancia nueva y segura:

bool tieneErrores = result.Errors.Count > 0;
var erroresVacios = ErrorCollection.Empty; // siempre devuelve una instancia nueva

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 (dominio semántico).
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.
Forbidden Seguridad El servidor entendió la petición pero rehúsa autorizarla (403).
NotFound Error El recurso solicitado no fue encontrado (404).
Conflict Error La petición entra en conflicto con el estado actual del servidor (409).
UnprocessableEntity Error Errores semánticos en la petición (422).
TooManyRequests Error Límite de peticiones excedido (429).
ServiceUnavailable Error El servidor no está listo para manejar la petición (503).
ChangePassword Seguridad El usuario debe cambiar su contraseña.
Error, Exception Error Error genérico o una excepción no controlada.

Extensiones de StatusResult

Importa Pitasoft.Result.Extensions para usar clasificación semántica sobre valores de StatusResult:

if (resultado.Status.IsSuccess())             { /* Ok / Added / Updated / Deleted */ }
if (resultado.Status.IsInfrastructureError()) { /* DatabaseError / ConcurrencyError / ConnectionError / HttpError / ServiceUnavailable */ }
if (resultado.Status.IsBusinessError())       { /* ValidationError / DataError / NoExist / Unauthorized / Forbidden / NotFound / Conflict / UnprocessableEntity / ChangePassword */ }
if (resultado.Status.IsTransientError())      { /* Error / Exception / CancelOperation / ConcurrencyError / ConnectionError / HttpError / TooManyRequests / ServiceUnavailable */ }
if (resultado.Status.IsError())                { /* Estados de error generales o especializados (Validación, Datos, BD, Concurrencia, HTTP, Excepción, etc.) */ }

Son especialmente útiles en middleware o políticas de reintento para decidir cómo manejar un fallo sin inspeccionar el valor de Status directamente.

Extensiones

Importa Pitasoft.Result.Extensions para usar estos métodos de ayuda en cualquier IResult:

  • result.IsSuccess / result.IsFailure: Propiedades disponibles directamente en cada instancia de resultado, sin necesidad de llamada a extensión.
  • result.IsSuccess(): Devuelve true si el estado es Ok, Added, Updated o Deleted.
  • result.IsError(): Devuelve true para la mayoría de los estados relacionados con errores.
  • result.IsFailure(): Devuelve true para cualquier estado no exitoso, excluyendo None, NoExist y Warning. Cubre Unauthorized, Exception, CancelOperation y más.
  • result.IsOk(): Devuelve true si el estado es exactamente Ok.
  • result.IsWarning(): Devuelve true si el estado es Warning.
  • result.IsNotExist(): Devuelve true si el recurso no fue encontrado (NoExist).
  • result.IsUnauthorized(): Devuelve true si el estado es Unauthorized.
  • result.IsForbidden() / result.IsNotFound() / result.IsConflict() / result.IsTooManyRequests() / result.IsUnprocessableEntity() / result.IsServiceUnavailable(): Comprobaciones semánticas para estados de error tipo HTTP.
  • result.HasErrors(): Devuelve true si hay algún error en la colección Errors.
  • result.IsSuccessOrNotExist(): Útil para operaciones "Delete" donde ambos casos suelen manejarse de forma similar.
  • result.IsSuccessOrWarning(): Devuelve true para estados exitosos o de advertencia.
  • Materialize() / MaterializeAsync(): Convierte IEnumerable<T> o IAsyncEnumerable<T> a ResultEntities<T> o ResultPaged<T>.
  • UpdateTimestamp(): Actualiza manualmente el CalculationTime a la hora UTC actual.
  • ToPaged(): Convierte un resultado a ResultPaged<T> preservando el estado.
  • ToResultEntity() / ToResultEntities(): Convierte entre tipos de resultado preservando metadatos.
  • ToOkResult() / ToAddedResult() / ToUpdatedResult() / ToDeletedResult(): Convierte una entidad en un ResultEntity<T> con el estado de éxito correspondiente.

API Funcional (Match, Map, Bind, Ensure, EnsureAsync, Recover, Tap, TapAsync)

Importa Pitasoft.Result.Extensions para usar transformaciones de estilo funcional.

Match — bifurcar según éxito o error

Disponible para Result, ResultEntity<T>, ResultEntities<T> y ResultPaged<T>:

// Result
string msg = resultado.Match(
    onSuccess: () => "Completado",
    onFailure: r => $"Error: {r.Status}");

// ResultEntity<T>
var dto = userResult.Match(
    onSuccess: () => mapper.ToDto(userResult.Entity),
    onFailure: r => throw new InvalidOperationException(r.Status.ToString()));

// ResultEntities<T> / ResultPaged<T>
var vista = pagedResult.Match(
    onSuccess: () => ConstruirVista(pagedResult),
    onFailure: r => VistaError(r));
MatchAsync — bifurcación asíncrona según éxito o error

Disponible para Result, ResultEntity<T>, ResultEntities<T> y ResultPaged<T>:

var respuesta = await pagedResult.MatchAsync(
    onSuccess: async p => await ConstruirRespuestaPaginadaAsync(p),
    onFailure: async r => await ManejarFalloAsync(r));
Map — transformar la entidad si el resultado es exitoso

Disponible para ResultEntity<T>, ResultEntities<T> y ResultPaged<T>:

ResultEntity<UserDto>   dto    = userResult.Map(user => mapper.ToDto(user));
ResultEntities<UserDto> dtos   = usersResult.Map(user => mapper.ToDto(user));
ResultPaged<UserDto>    paged  = pagedResult.Map(user => mapper.ToDto(user));
MapAsync — encadenar transformaciones asíncronas
ResultEntity<UserDto>   dto    = await userResult.MapAsync(async u => await EnriquecerAsync(u));
ResultEntities<UserDto> dtos   = await usersResult.MapAsync(async u => await EnriquecerAsync(u));
ResultPaged<UserDto>    paged  = await pagedResult.MapAsync(async u => await EnriquecerAsync(u));
Tap — ejecutar una acción sin cambiar el resultado
result.Tap(() => _logger.LogInformation("Operación exitosa"))
      .Tap(user => _cache.Set(user));
TapAsync — efecto secundario asíncrono sin cambiar el resultado

Disponible para cualquier tipo de resultado (Result, ResultEntity<T>, ResultEntities<T>, ResultPaged<T>):

var result = await ObtenerUsuarioAsync(id)
    .TapAsync(async () => await _auditoría.RegistrarAsync("usuario obtenido"));

// Variante tipada
var result = await ObtenerUsuarioAsync(id)
    .TapAsync(async user => await _cache.SetAsync(user));
Bind — encadenar operaciones que devuelven resultados (FlatMap)

Disponible para ResultEntity<T>, ResultEntities<T> y ResultPaged<T>:

ResultEntity<Order> result = GetUser(id)
    .Bind(user => CreateOrder(user));

ResultPaged<OrderDto> paged = GetPagedOrders(page, size)
    .Bind(o => EnrichOrder(o));
Ensure — validar una condición

Sobre Result (base):

Result result = VerificarPermisos(userId)
    .Ensure(() => _cuota.TieneCapacidad(), "Cuota superada", "Cuota");

Sobre ResultEntity<T>:

ResultEntity<User> result = GetUser(id)
    .Ensure(u => u.Age >= 18, "El usuario debe ser mayor de edad", "Edad");
EnsureAsync — validar una condición asíncrona

Realiza validaciones que requieren I/O, como comprobaciones en base de datos, dentro de la cadena fluida:

ResultEntity<User> result = await GetUser(id)
    .EnsureAsync(async u => await _repo.EsEmailUnicoAsync(u.Email), "Email ya existe", "Email");
Recover — proporcionar un valor de respaldo en caso de fallo

Maneja fallos de forma segura proporcionando un valor por defecto o una función de recuperación:

// Valor por defecto simple
User user = GetUser(id).Recover(new User { Name = "Invitado" });

// Función de recuperación
User user = GetUser(id).Recover(r => new User { Name = $"Invitado (Error: {r.Status})" });

Métodos Seguros ante Excepciones (Try)

Todas las variantes Try capturan excepciones automáticamente. OperationCanceledException se mapea a CancelOperation en lugar de Exception. Ahora puedes proporcionar opcionalmente una función mapException para transformar excepciones específicas en códigos StatusResult significativos.

Método Retorna Notas
Result.Try(action, deep?, mapEx?) Result Síncrono, sin valor de retorno
Result.Try<T>(func, deep?, mapEx?) ResultEntity<T> Síncrono, devuelve entidad
Result.TryAsync(action, ct?, deep?, mapEx?) Task<Result> Asíncrono, soporta CancellationToken
Result.TryAsync<T>(func, ct?, deep?, mapEx?) Task<ResultEntity<T>> Entidad asíncrona, soporta CancellationToken
Result.TryEntities<T>(func, deep?, mapEx?) ResultEntities<T> Colección síncrona
Result.TryEntitiesAsync<T>(func, deep?, mapEx?) Task<ResultEntities<T>> Colección asíncrona
Result.TryPagedAsync<T>(func, page, pageSize, deep?, mapEx?) Task<ResultPaged<T>> Colección paginada asíncrona
// Mapeo automático de excepciones a códigos de estado
var result = Result.Try(() => _repo.Get(id), 
    mapException: ex => ex is KeyNotFoundException ? StatusResult.NotFound : StatusResult.Error);

// Soporte CancellationToken — OperationCanceledException → CancelOperation
var resultado = await Result.TryAsync(
    async ct => await _servicio.ProcesarAsync(ct),
    cancellationToken);

// Colección
ResultEntities<Product> productos = Result.TryEntities(() => _repo.ObtenerTodos());

// Paginado
ResultPaged<Product> pagina = await Result.TryPagedAsync(
    async () => await _repo.ObtenerPaginadoAsync(1, 20),
    page: 1, pageSize: 20);

Combinar Resultados

Agrega múltiples instancias de Result en una sola. El resultado combinado es Ok si todos tienen éxito; en caso contrario acumula todos los errores de los fallidos.

// Síncrono
Result combinado = Result.Combine(
    ValidarNombre(dto.Name),
    ValidarEmail(dto.Email),
    ValidarEdad(dto.Age));

// Asíncrono
Result combinado = await Result.CombineAsync(
    ValidarNombreAsync(dto.Name),
    ValidarEmailAsync(dto.Email));

Parámetros de Paginación y Entidad

Usa PagingParameters para recibir la entrada de paginación y EntityParameters para filtrado, búsqueda y ordenación.

GetParameters() devuelve IEnumerable<KeyValuePair<string, string>>, lo que permite múltiples valores para la misma clave (útil para query strings tipo array como ?tag=a&tag=b):

public ResultPaged<User> GetUsers(EntityParameters parametros)
{
    // parametros.Page, parametros.PageSize, parametros.Search, parametros.Query, parametros.Order, parametros.Attrs
    var (users, total) = _repository.GetAll(parametros);
    return Result.OkPaged(users, total, parametros.Page, parametros.PageSize);
}

// Convertir a Dictionary cuando un mapa de valor único es suficiente
var dict = parametros.GetParameters().ToDictionary(kv => kv.Key, kv => kv.Value);

// O usar directamente con constructores de query de HttpClient que acepten IEnumerable<KVP>
var query = QueryString.Create(parametros.GetParameters());

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
  • Caché de Instancias (Singleton): Los estados comunes de Result (NotExists(), ValidationError()) ahora utilizan instancias estáticas reutilizables. Esto reduce el costo de asignación de $O(n)$ a $O(1)$ para estas llamadas frecuentes.
  • Optimización de Parámetros: PagingParameters.GetParameters() y EntityParameters.GetParameters() ahora utilizan yield return en lugar de crear listas temporales, mejorando la eficiencia de memoria de $O(n)$ a $O(1)$ de espacio.
  • Eficiencia en Colecciones: ResultEntities<T> y ResultPaged<T> utilizan IReadOnlyList<T> para la propiedad Entities. Las colecciones se materializan eficientemente en una lista solo cuando es necesario.
  • Aggressive Inlining: Las factorías estáticas y los métodos de extensión están anotados con AggressiveInlining para minimizar el overhead.
  • Iteración Directa: Soportada mediante foreach. Internamente, su enumerador delega en la colección subyacente sin usar yield, evitando asignaciones extra de la máquina de estados.
  • Carga JSON: ResultPaged<T> ignora automáticamente las propiedades calculadas como TotalPages, HasNextPage y HasPreviousPage durante la serialización para reducir el tamaño del payload.
  • Propiedades IsSuccess / IsFailure: Usa estas propiedades en lugar de llamadas a métodos de extensión en rutas de código críticas — son lecturas de propiedad directas sin sobrecarga de despacho de métodos.
  • Gestión de Excepciones: Al crear resultados de error desde excepciones manualmente, usa deep: false salvo que necesites explícitamente el detalle de inner exceptions para reducir asignaciones en ErrorCollection.

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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 157 4/19/2026
7.3.2 218 4/3/2026
7.3.1 357 3/26/2026
7.2.9 160 3/24/2026
7.2.8 138 3/23/2026
7.2.7 140 3/22/2026
7.2.6 170 3/20/2026
7.2.5 142 3/20/2026
7.2.4 206 3/13/2026
7.2.3 143 3/11/2026
7.2.2 164 3/10/2026
7.2.1 155 3/9/2026
7.1.4 194 3/2/2026
7.1.3 181 2/24/2026
7.1.2 148 2/24/2026
7.1.1 189 2/23/2026
7.0.2 174 2/12/2026
7.0.1 201 1/26/2026
6.5.7 391 5/27/2025
6.5.6 359 5/27/2025
Loading failed