Acontplus.Utilities 1.3.10

dotnet add package Acontplus.Utilities --version 1.3.10
                    
NuGet\Install-Package Acontplus.Utilities -Version 1.3.10
                    
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="Acontplus.Utilities" Version="1.3.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Acontplus.Utilities" Version="1.3.10" />
                    
Directory.Packages.props
<PackageReference Include="Acontplus.Utilities" />
                    
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 Acontplus.Utilities --version 1.3.10
                    
#r "nuget: Acontplus.Utilities, 1.3.10"
                    
#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 Acontplus.Utilities@1.3.10
                    
#: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=Acontplus.Utilities&version=1.3.10
                    
Install as a Cake Addin
#tool nuget:?package=Acontplus.Utilities&version=1.3.10
                    
Install as a Cake Tool

Acontplus.Utilities

NuGet .NET License

A comprehensive .NET 9+ utility library providing common functionality for business applications. Async, extension methods, minimal API support, and more.

🚀 Features

  • API Response Extensions - Comprehensive Result<T> to IActionResult/IResult conversions with domain error handling, pagination, and warnings support
  • Domain Extensions - Consolidated domain-to-API conversion logic for clean architectural separation
  • Encryption - Data encryption/decryption utilities with BCrypt support
  • External Validations - Third-party validation integrations and data validation helpers
  • Custom Logging - Enhanced logging capabilities
  • Enum Extensions - Enhanced enum functionality
  • Picture Helper - Image processing utilities
  • Text Handlers - Text manipulation and processing
  • Pagination & Metadata - Helpers for API metadata, pagination, and diagnostics
  • Data Utilities - JSON manipulation, DataTable mapping, and data converters
  • Object Mapping - AutoMapper-like object mapping utilities
  • File Extensions - File handling, MIME types, and compression utilities

Note: For barcode generation features, see the separate Acontplus.Barcode package which provides QR codes, Code 128, EAN-13, and other barcode formats.

🏗️ Architecture & Domain Extensions

This package provides a clean architectural separation between domain logic (in Acontplus.Core) and API conversion logic (in Acontplus.Utilities). All domain-to-API conversions are consolidated here for maintainability.

Consolidated Domain Extensions

The following domain extensions have been consolidated into this package for clean separation:

  • Domain Error Extensions: Convert DomainError, DomainErrors, and Result<T> to ApiResponse<T> with proper HTTP status codes
  • Pagination Extensions: Build pagination links for PagedResult<T> with navigation metadata
  • Domain Warnings Extensions: Handle DomainWarnings and SuccessWithWarnings<T> conversions
  • Error Analysis: Severity-based error prioritization and aggregate error messaging

Result-to-API Conversion Pipeline

// Domain layer (Acontplus.Core) - Pure domain logic
public Result<User, DomainError> GetUser(int id) { /* domain logic */ }

// API layer (Acontplus.Utilities) - Clean conversions
public IActionResult GetUser(int id)
{
    var result = _userService.GetUser(id);
    return result.ToActionResult(); // Automatic conversion with error handling
}

// Minimal API
app.MapGet("/users/{id}", (int id) =>
{
    var result = userService.GetUser(id);
    return result.ToMinimalApiResult(); // Clean, type-safe responses
});

📦 Installation

NuGet Package Manager

Install-Package Acontplus.Utilities

.NET CLI

dotnet add package Acontplus.Utilities

PackageReference

<ItemGroup>
  <PackageReference Include="Acontplus.Utilities" Version="1.3.7" />
</ItemGroup>

🎯 Quick Start

1. Consolidated API Response Extensions

The ResultApiExtensions class provides comprehensive conversions from domain Result<T> types to API responses:

using Acontplus.Utilities.Extensions;

// Domain result to API response (automatic error handling)
public IActionResult GetUser(int id)
{
    var result = userService.GetUser(id);
    return result.ToActionResult(); // Handles success/error automatically
}

// With custom success message
var result = userService.CreateUser(user);
return result.ToActionResult("User created successfully");

// Minimal API support
app.MapGet("/users/{id}", (int id) =>
{
    var result = userService.GetUser(id);
    return result.ToMinimalApiResult();
});

2. Domain Error Handling

using Acontplus.Utilities.Extensions;

// Automatic domain error to API response conversion
public IActionResult UpdateUser(int id, UserDto dto)
{
    var result = userService.UpdateUser(id, dto);
    return result.ToActionResult(); // DomainError automatically becomes 400/404/500 etc.
}

// Multiple errors with severity-based HTTP status
var errors = DomainErrors.Multiple(new[] {
    DomainError.Validation("EMAIL_INVALID", "Invalid email format"),
    DomainError.NotFound("USER_NOT_FOUND", "User not found")
});
return errors.ToActionResult<UserDto>();
using Acontplus.Utilities.Extensions;

// Build pagination links automatically
public IActionResult GetUsers(PaginationQuery query)
{
    var result = userService.GetUsers(query);
    var links = result.BuildPaginationLinks("/api/users", query.PageSize);
    return result.ToActionResult();
}

4. Encryption Example

var encryptionService = new SensitiveDataEncryptionService();
byte[] encrypted = await encryptionService.EncryptToBytesAsync("password", "data");
string decrypted = await encryptionService.DecryptFromBytesAsync("password", encrypted);

5. Pagination Metadata Example

var metadata = new Dictionary<string, object>()
    .WithPagination(page: 1, pageSize: 10, totalItems: 100);

📄 Core Examples

PaginationQuery with Minimal APIs

The PaginationQuery record provides automatic parameter binding in minimal APIs with support for multiple filters, sorting, and pagination.

Backend (Minimal API)
// Program.cs or endpoint definition
app.MapGet("/api/users", async (PaginationQuery pagination, IUserService userService) =>
{
    var result = await userService.GetPaginatedUsersAsync(pagination);
    return Results.Ok(result);
})
.WithName("GetUsers")
.WithOpenApi();

// Service implementation
public async Task<PagedResult<UserDto>> GetPaginatedUsersAsync(PaginationQuery pagination)
{
    var spParameters = new Dictionary<string, object>
    {
        ["@PageIndex"] = pagination.PageIndex,
        ["@PageSize"] = pagination.PageSize,
        ["@SearchTerm"] = pagination.SearchTerm ?? (object)DBNull.Value,
        ["@SortBy"] = pagination.SortBy ?? "CreatedAt",
        ["@SortDirection"] = (pagination.SortDirection ?? SortDirection.Asc).ToString()
    };

    // Add filters from PaginationQuery.Filters
    if (pagination.Filters != null)
    {
        foreach (var filter in pagination.Filters)
        {
            var paramName = $"@{filter.Key}";
            spParameters[paramName] = filter.Value ?? DBNull.Value;
        }
    }

    // Execute stored procedure with all parameters
    var dataSet = await _adoRepository.GetDataSetAsync("sp_GetPaginatedUsers", spParameters);
    // Process results and return PagedResult
}
Frontend (JavaScript/TypeScript)
// API client function
async function getUsers(filters: UserFilters = {}) {
    const params = new URLSearchParams({
        pageIndex: '1',
        pageSize: '20',
        searchTerm: filters.searchTerm || '',
        sortBy: 'createdAt',
        sortDirection: 'desc'
    });

    // Add filters
    if (filters.status) params.append('filters[status]', filters.status);
    if (filters.role) params.append('filters[role]', filters.role);
    if (filters.isActive !== undefined) params.append('filters[isActive]', filters.isActive.toString());
    if (filters.createdDate) params.append('filters[createdDate]', filters.createdDate);

    const response = await fetch(`/api/users?${params.toString()}`);
    return response.json();
}

// Usage examples
const users = await getUsers({
    searchTerm: 'john',
    status: 'active',
    role: 'admin',
    isActive: true
});

// URL generated: /api/users?pageIndex=1&pageSize=20&searchTerm=john&sortBy=createdAt&sortDirection=desc&filters[status]=active&filters[role]=admin&filters[isActive]=true
React Component Example
import React, { useState, useEffect } from 'react';

interface UserFilters {
    searchTerm?: string;
    status?: string;
    role?: string;
    isActive?: boolean;
}

const UserList: React.FC = () => {
    const [users, setUsers] = useState([]);
    const [filters, setFilters] = useState<UserFilters>({});
    const [pagination, setPagination] = useState({ pageIndex: 1, pageSize: 20 });

    const fetchUsers = async () => {
        const params = new URLSearchParams({
            pageIndex: pagination.pageIndex.toString(),
            pageSize: pagination.pageSize.toString(),
            sortBy: 'createdAt',
            sortDirection: 'desc'
        });

        // Add search term
        if (filters.searchTerm) {
            params.append('searchTerm', filters.searchTerm);
        }

        // Add filters
        Object.entries(filters).forEach(([key, value]) => {
            if (value !== undefined && key !== 'searchTerm') {
                params.append(`filters[${key}]`, value.toString());
            }
        });

        const response = await fetch(`/api/users?${params.toString()}`);
        const data = await response.json();
        setUsers(data.items);
    };

    useEffect(() => {
        fetchUsers();
    }, [filters, pagination]);

    return (
        <div>
            {/* Filter controls */}
            <input
                type="text"
                placeholder="Search users..."
                onChange={(e) => setFilters(prev => ({ ...prev, searchTerm: e.target.value }))}
            />
            <select onChange={(e) => setFilters(prev => ({ ...prev, status: e.target.value }))}>
                <option value="">All Status</option>
                <option value="active">Active</option>
                <option value="inactive">Inactive</option>
            </select>
            
            {/* User list */}
            {users.map(user => (
                <div key={user.id}>{user.name}</div>
            ))}
        </div>
    );
};
Query Parameter Examples
# Basic pagination
GET /api/users?pageIndex=1&pageSize=20

# With search and sorting
GET /api/users?pageIndex=1&pageSize=20&searchTerm=john&sortBy=name&sortDirection=asc

# With multiple filters
GET /api/users?pageIndex=1&pageSize=20&filters[status]=active&filters[role]=admin&filters[isActive]=true

# Complex filter combinations
GET /api/users?pageIndex=1&pageSize=20&searchTerm=john&filters[status]=active&filters[role]=admin&filters[createdDate]=2024-01-01&filters[departments][]=IT&filters[departments][]=HR

Result & API Response Patterns with Usuario Module

Controller Usage
[HttpGet("{id:int}")]
public async Task<IActionResult> GetUsuario(int id)
{
    var result = await _usuarioService.GetByIdAsync(id);
    return result.ToGetActionResult();
}

[HttpPost]
public async Task<IActionResult> CreateUsuario([FromBody] UsuarioDto dto)
{
    var usuario = ObjectMapper.Map<UsuarioDto, Usuario>(dto);
    var result = await _usuarioService.AddAsync(usuario);
    if (result.IsSuccess && result.Value is not null)
    {
        var locationUri = $"/api/Usuario/{result.Value.Id}";
        return ApiResponse<Usuario>.Success(result.Value, new ApiResponseOptions { Message = "Usuario creado exitosamente." }).ToActionResult();
    }
    return result.ToActionResult();
}

[HttpPut("{id:int}")]
public async Task<IActionResult> UpdateUsuario(int id, [FromBody] UsuarioDto dto)
{
    var usuario = ObjectMapper.Map<UsuarioDto, Usuario>(dto);
    var result = await _usuarioService.UpdateAsync(id, usuario);
    if (result.IsSuccess)
    {
        return ApiResponse<Usuario>.Success(result.Value, new ApiResponseOptions { Message = "Usuario actualizado correctamente." }).ToActionResult();
    }
    return result.ToActionResult();
}

[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteUsuario(int id)
{
    var result = await _usuarioService.DeleteAsync(id);
    return result.ToDeleteActionResult();
}
Minimal API Usage
app.MapGet("/usuarios/{id:int}", async (int id, IUsuarioService service) =>
{
    var result = await service.GetByIdAsync(id);
    return result.ToMinimalApiResult();
});
Service Layer Example
public async Task<Result<Usuario, DomainErrors>> AddAsync(Usuario usuario)
{
    var errors = new List<DomainError>();
    if (string.IsNullOrWhiteSpace(usuario.Username))
        errors.Add(DomainError.Validation("USERNAME_REQUIRED", "Username is required"));
    if (string.IsNullOrWhiteSpace(usuario.Email))
        errors.Add(DomainError.Validation("EMAIL_REQUIRED", "Email is required"));

    if (errors.Count > 0)
        return DomainErrors.Multiple(errors);

    // ... check for existing user, add, etc.
}

🗃️ Data Utilities

DataConverters

using Acontplus.Utilities.Data;

// Convert DataTable to JSON
string json = DataConverters.DataTableToJson(myDataTable);

// Convert DataSet to JSON
string json = DataConverters.DataSetToJson(myDataSet);

// Convert JSON to DataTable
DataTable table = DataConverters.JsonToDataTable(jsonString);

// Serialize any object (with DataTable/DataSet support)
string json = DataConverters.SerializeObjectCustom(myObject);

// Serialize and sanitize complex objects
string json = DataConverters.SerializeSanitizedData(myObject);

DataTableMapper

using Acontplus.Utilities.Data;

// Map a DataRow to a strongly-typed model
var model = DataTableMapper.MapDataRowToModel<MyModel>(dataRow);

// Map a DataTable to a list of models
List<MyModel> models = DataTableMapper.MapDataTableToList<MyModel>(dataTable);

🧩 JSON Utilities

JsonHelper

using Acontplus.Utilities.Json;

// Validate JSON
var result = JsonHelper.ValidateJson(jsonString);
if (!result.IsValid) Console.WriteLine(result.ErrorMessage);

// Get a property value from JSON
string? value = JsonHelper.GetJsonProperty<string>(jsonString, "propertyName");

// Merge two JSON objects
string merged = JsonHelper.MergeJson(json1, json2);

// Compare two JSON strings (ignoring property order)
bool areEqual = JsonHelper.AreEqual(json1, json2);

JsonManipulationExtensions

using Acontplus.Utilities.Json;

// Validate JSON using extension
bool isValid = jsonString.IsValidJson();

// Get property value using extension
int? id = jsonString.GetJsonProperty<int>("id");

// Merge JSON using extension
string merged = json1.MergeJson(json2);

// Compare JSON using extension
bool equal = json1.JsonEquals(json2);

🔄 Object Mapping

ObjectMapper

using Acontplus.Utilities.Mapping;

// Map between objects (AutoMapper-like)
var target = ObjectMapper.Map<SourceType, TargetType>(sourceObject);

// Configure custom mapping
ObjectMapper.CreateMap<SourceType, TargetType>()
    .ForMember(dest => dest.SomeProperty, src => src.OtherProperty)
    .Ignore(dest => dest.IgnoredProperty);

// Map with configuration
var mapped = ObjectMapper.Map<SourceType, TargetType>(sourceObject);

🔧 Advanced Usage

File Name Sanitization

string safeName = FileExtensions.SanitizeFileName("my*illegal:file?.txt");

Base64 Conversion

string base64 = FileExtensions.GetBase64FromByte(myBytes);

Compression Utilities

byte[] compressed = CompressionUtils.CompressGZip(data);
byte[] decompressed = CompressionUtils.DecompressGZip(compressed);

📚 API Documentation

Consolidated Domain Extensions

  • ResultApiExtensions - Comprehensive Result<T> to IActionResult/IResult conversions with domain error handling
  • DomainErrorExtensions - Convert DomainError/DomainErrors to ApiResponse with HTTP status mapping
  • PagedResultExtensions - Build pagination links and metadata for API responses
  • DomainWarningsExtensions - Handle DomainWarnings and SuccessWithWarnings conversions

Core Utilities

  • SensitiveDataEncryptionService - AES encryption/decryption helpers with BCrypt support
  • FileExtensions - File name sanitization and byte array utilities
  • CompressionUtils - GZip/Deflate compression helpers
  • TextHandlers - String formatting and splitting utilities
  • DirectoryHelper / EnvironmentHelper - Runtime and environment utilities
  • DataConverters - DataTable/DataSet JSON conversion
  • JsonHelper - JSON validation and manipulation
  • ObjectMapper - Object-to-object mapping utilities

🤝 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

Development Setup

git clone https://github.com/Acontplus-S-A-S/acontplus-dotnet-libs.git
cd acontplus-dotnet-libs
dotnet restore
dotnet build

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🆘 Support

  • 📧 Email: proyectos@acontplus.com
  • 🐛 Issues: GitHub Issues
  • 📖 Documentation: Wiki

👨‍💻 Author

Ivan Paz - @iferpaz7

🏢 Company

Acontplus S.A.S. - Software solutions


Built with ❤️ for the .NET community

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

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Acontplus.Utilities:

Package Downloads
Acontplus.Notifications

Comprehensive library for notification services. Includes email (MailKit, Amazon SES), WhatsApp, push notifications, templating with Scriban, queue management, and enterprise-ready delivery patterns for cloud-native applications.

Acontplus.FactElect

Complete library for electronic invoicing and SRI integration in Ecuador. Includes models, services, XML validation, SRI web service support, and embedded XSD schemas for compliance.

Acontplus.Reports

Advanced library for comprehensive report generation and management. Includes RDLC report processing, PDF/Excel export capabilities, ReportViewer integration, template support, and enterprise-ready reporting patterns for business applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.10 28 9/14/2025
1.3.9 28 9/14/2025
1.3.8 32 9/14/2025
1.3.7 138 9/10/2025
1.3.6 158 8/21/2025
1.3.5 136 8/19/2025
1.3.4 146 8/8/2025
1.3.3 131 8/8/2025
1.3.2 213 8/7/2025
1.3.1 203 8/7/2025
1.3.0 204 8/7/2025
1.2.5 230 8/5/2025
1.2.4 522 7/23/2025
1.2.3 113 7/18/2025
1.2.2 155 7/15/2025
1.2.1 157 7/15/2025
1.2.0 154 7/14/2025
1.1.4 158 7/14/2025
1.1.3 108 7/11/2025
1.1.2 104 7/11/2025
1.1.1 158 7/10/2025
1.1.0 152 7/10/2025
1.0.13 147 7/10/2025
1.0.12 162 7/9/2025
1.0.11 189 7/7/2025
1.0.10 152 7/6/2025
1.0.9 158 7/6/2025
1.0.8 109 7/4/2025
1.0.7 161 7/3/2025
1.0.6 162 7/2/2025
1.0.5 161 7/2/2025
1.0.4 158 7/1/2025

Fixed compilation errors in ResultApiExtensions, updated README to remove incorrect barcode generation claims, enhanced documentation with correct feature descriptions and better examples.