RepletoryLib.Common 1.0.0

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

RepletoryLib.Common

Common types, extensions, and base classes for the RepletoryLib ecosystem.

Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.

NuGet .NET 10 License: MIT


Overview

RepletoryLib.Common is the foundational package of the RepletoryLib ecosystem. It provides the shared types, patterns, and conventions that all other RepletoryLib packages build upon.

This package includes the Result pattern for consistent operation outcomes, a BaseEntity with built-in audit tracking, multi-tenancy, and soft-delete support, standardized exceptions for error handling, pagination models for paginated queries, and a set of commonly used extension methods.

RepletoryLib.Common has zero external dependencies beyond the .NET runtime, making it lightweight and safe to include in any project layer -- from domain models to API controllers.

Key Features

  • Result pattern -- Result<T> and Result for explicit success/failure flows without exceptions
  • BaseEntity -- Abstract base class with identity, audit fields, soft-delete, and multi-tenancy
  • Standardized exceptions -- AppException and ValidationException with HTTP status codes
  • Error response model -- ErrorResponse for consistent API error payloads
  • Pagination -- PagedRequest, PagedResult<T> for offset-based pagination
  • Extension methods -- String, DateTime, and Guid helpers
  • Service interfaces -- ICurrentUserService and ICurrentTenantService contracts

Installation

dotnet add package RepletoryLib.Common

Or add to your .csproj:

<PackageReference Include="RepletoryLib.Common" Version="1.0.0" />

Note: RepletoryLib packages are published to a local BaGet feed. See the main repository README for feed configuration.


Usage Examples

Result Pattern

Use Result<T> and Result to represent the outcome of operations without throwing exceptions for expected failure cases.

using RepletoryLib.Common.Models;

public class OrderService
{
    public Result<Order> GetOrder(Guid id)
    {
        var order = _repository.FindById(id);

        if (order is null)
            return Result<Order>.NotFound($"Order {id} not found");

        if (order.TenantId != _currentTenant.TenantId)
            return Result<Order>.Unauthorized("Access denied to this order");

        return Result<Order>.Success(order);
    }

    public Result CancelOrder(Guid id)
    {
        var order = _repository.FindById(id);

        if (order is null)
            return Result.NotFound($"Order {id} not found");

        if (order.Status == OrderStatus.Shipped)
            return Result.Failure("Cannot cancel a shipped order", 409);

        order.Status = OrderStatus.Cancelled;
        _repository.Update(order);

        return Result.Success();
    }
}

Consuming results in a controller:

[HttpGet("{id}")]
public IActionResult GetOrder(Guid id)
{
    var result = _orderService.GetOrder(id);

    if (!result.IsSuccess)
        return StatusCode(result.StatusCode, new { error = result.Error });

    return Ok(result.Data);
}
Factory Methods
Method Status Code Usage
Result<T>.Success(data) 200 Operation completed successfully
Result<T>.Failure(error, code?) 400 (default) General failure with optional custom code
Result<T>.NotFound(error) 404 Resource not found
Result<T>.Unauthorized(error) 401 Insufficient permissions

The non-generic Result exposes the same factory methods for operations that don't return data.


BaseEntity

All domain entities should inherit from BaseEntity to gain identity, audit tracking, multi-tenancy, and soft-delete capabilities.

using RepletoryLib.Common.Entities;

public class Customer : BaseEntity
{
    public string FullName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string PhoneNumber { get; set; } = string.Empty;
}

BaseEntity provides the following properties automatically:

Property Type Default Description
Id Guid Guid.NewGuid() Unique identifier
CreatedAt DateTime DateTime.UtcNow UTC creation timestamp
UpdatedAt DateTime? null UTC last update timestamp
CreatedBy string? null User ID of creator
UpdatedBy string? null User ID of last updater
TenantId Guid? null Multi-tenancy identifier
IsDeleted bool false Soft-delete flag
DeletedAt DateTime? null UTC deletion timestamp
DeletedBy string? null User ID of deleter

Exceptions

Use AppException for application-level errors with HTTP status codes, and ValidationException for field-level validation failures.

using RepletoryLib.Common.Exceptions;

// General application exception with status code
throw new AppException("Payment gateway timeout", 502);

// Application exception with multiple error details
throw new AppException("Order creation failed", 400, new List<string>
{
    "Insufficient stock for item SKU-001",
    "Delivery address is outside service area"
});

// Validation exception with per-field errors
throw new ValidationException(new Dictionary<string, string[]>
{
    ["Email"] = ["Email is required", "Email format is invalid"],
    ["PhoneNumber"] = ["Phone number must be a valid SA number"]
});

ValidationException automatically sets the status code to 422 (Unprocessable Entity) and flattens field errors into the Errors list in "FieldName: error message" format.


Error Response Model

Use ErrorResponse as the standard API error payload:

using RepletoryLib.Common.Models;

var error = new ErrorResponse
{
    StatusCode = 404,
    Message = "Order not found",
    Errors = new List<string> { "No order exists with ID 550e8400" },
    TraceId = Activity.Current?.Id
};

// Serializes to:
// {
//   "statusCode": 404,
//   "message": "Order not found",
//   "errors": ["No order exists with ID 550e8400"],
//   "traceId": "00-abc123...",
//   "timestamp": "2025-01-15T10:30:00Z"
// }

Pagination

Use PagedRequest to capture pagination parameters and PagedResult<T> to return paginated collections.

using RepletoryLib.Common.Models;

// Incoming request
var request = new PagedRequest
{
    Page = 2,
    PageSize = 25,
    SortBy = "CreatedAt",
    SortDirection = SortDirection.Descending
};

// Build a paged result
var customers = await _dbContext.Customers
    .OrderByDescending(c => c.CreatedAt)
    .Skip((request.Page - 1) * request.PageSize)
    .Take(request.PageSize)
    .ToListAsync();

var totalCount = await _dbContext.Customers.CountAsync();

var result = new PagedResult<Customer>
{
    Items = customers,
    TotalCount = totalCount,
    Page = request.Page,
    PageSize = request.PageSize
};

// result.TotalPages  => computed automatically
// result.HasNextPage  => true if more pages exist
// result.HasPreviousPage => true if Page > 1

PagedRequest clamps Page to a minimum of 1 and PageSize between 1-100.


Service Interfaces

ICurrentUserService and ICurrentTenantService provide abstractions for accessing the authenticated user and active tenant. Implement these in your application layer.

using RepletoryLib.Common.Interfaces;

// Implementation example (typically registered in DI)
public class HttpCurrentUserService : ICurrentUserService
{
    private readonly IHttpContextAccessor _accessor;

    public HttpCurrentUserService(IHttpContextAccessor accessor) =>
        _accessor = accessor;

    public string UserId =>
        _accessor.HttpContext?.User.FindFirst("sub")?.Value ?? string.Empty;

    public string UserName =>
        _accessor.HttpContext?.User.FindFirst("name")?.Value ?? string.Empty;

    public Guid? TenantId =>
        Guid.TryParse(_accessor.HttpContext?.User.FindFirst("tenant_id")?.Value, out var id)
            ? id : null;

    public IReadOnlyList<string> Roles =>
        _accessor.HttpContext?.User.FindAll("role").Select(c => c.Value).ToList()
        ?? new List<string>();

    public bool HasPermission(string permission) =>
        _accessor.HttpContext?.User.HasClaim("permission", permission) ?? false;
}
// Using ICurrentUserService in a service
public class AuditService
{
    private readonly ICurrentUserService _user;

    public AuditService(ICurrentUserService user) => _user = user;

    public void StampEntity(BaseEntity entity)
    {
        entity.UpdatedBy = _user.UserId;
        entity.UpdatedAt = DateTime.UtcNow;
        entity.TenantId = _user.TenantId;
    }
}

Extension Methods

String Extensions
using RepletoryLib.Common.Extensions;

// URL-friendly slugs
"Hello World! C# is Great".ToSlug();  // "hello-world-c-is-great"

// Truncation with ellipsis
"A very long description text".Truncate(15);  // "A very long ..."

// Title case
"john doe".ToTitleCase();  // "John Doe"

// Null/empty check
string? name = null;
name.IsNullOrEmpty();  // true

// Email masking
"john.doe@example.com".MaskEmail();  // "j***@example.com"

// Phone masking
"+27823456789".MaskPhone();  // "+27***6789"
DateTime Extensions
using RepletoryLib.Common.Extensions;

var now = DateTime.UtcNow;

// Timezone conversion
now.ToSast();                              // South Africa Standard Time
now.ToTimezone("America/New_York");        // Eastern Time

// Day checks
now.IsWeekend();     // true if Saturday or Sunday
now.IsBusinessDay(); // true if Monday-Friday

// Range boundaries
now.StartOfDay();    // 2025-01-15 00:00:00.000
now.EndOfDay();      // 2025-01-15 23:59:59.999
now.StartOfWeek();   // Monday of the ISO week
now.StartOfMonth();  // 2025-01-01 00:00:00.000

// Ensure UTC kind
var local = DateTime.Now;
local.ToUtc();  // Sets DateTimeKind to UTC
Guid Extensions
using RepletoryLib.Common.Extensions;

var id = Guid.NewGuid();

id.IsEmpty();        // false
Guid.Empty.IsEmpty(); // true

id.ToShortString();  // "a1b2c3d4" (first 8 hex chars)

API Reference

Models

Type Description
Result<T> Generic result with Data, Error, StatusCode, and IsSuccess
Result Non-generic result with Error, StatusCode, and IsSuccess
PagedResult<T> Paginated collection with Items, TotalCount, Page, PageSize, computed TotalPages/HasNextPage/HasPreviousPage
PagedRequest Pagination input with Page, PageSize, SortBy, SortDirection
SortDirection Enum: Ascending, Descending
ErrorResponse Standardized error payload with StatusCode, Message, Errors, TraceId, Timestamp

Entities

Type Description
BaseEntity Abstract base class with Id, audit fields, soft-delete, and TenantId

Interfaces

Type Description
ICurrentUserService Access authenticated user's UserId, UserName, TenantId, Roles, HasPermission()
ICurrentTenantService Access current tenant's TenantId and TenantName

Exceptions

Type Base Default Status Description
AppException Exception 500 Application error with status code and error list
ValidationException AppException 422 Validation failure with per-field errors

Integration with Other RepletoryLib Packages

RepletoryLib.Common is the root dependency for virtually every package in the ecosystem:

Package How It Uses Common
RepletoryLib.Data.EntityFramework BaseEntity as the entity base, ICurrentUserService for audit stamping
RepletoryLib.Data.Interceptors BaseEntity properties for interceptor processing
RepletoryLib.Caching.Abstractions Builds on Common's interface patterns
RepletoryLib.Messaging.Abstractions Builds on Common's interface patterns
RepletoryLib.Api.Middleware AppException and ErrorResponse for exception handling
RepletoryLib.Auth.Jwt ICurrentUserService for JWT claim extraction
RepletoryLib.Utilities.Pagination PagedResult<T> and PagedRequest for query extensions
RepletoryLib.Testing Provides mock implementations of ICurrentUserService and ICurrentTenantService

Testing

Use RepletoryLib.Testing for mock implementations:

using RepletoryLib.Testing;

public class OrderServiceTests : TestBase
{
    [Fact]
    public void GetOrder_returns_not_found()
    {
        Setup();
        MockUser.UserId = "user-123";

        var service = new OrderService(MockUser);
        var result = service.GetOrder(Guid.NewGuid());

        result.IsSuccess.Should().BeFalse();
        result.StatusCode.Should().Be(404);
    }
}

For Result<T> assertions, use RepletoryLib.Testing's FluentAssertions extensions:

result.Should().BeSuccess().And.HaveData(expectedOrder);
result.Should().BeFailure().And.HaveStatusCode(404);

Troubleshooting

Issue Solution
Result<T>.Data is null on success Ensure you're passing a non-null value to Result<T>.Success(data)
PagedRequest.PageSize capped at 100 By design -- the setter clamps values between 1-100 to prevent excessive queries
BaseEntity.Id generates a new GUID each time This is the default. Override in your entity constructor or let your ORM handle ID assignment
ValidationException flattens errors Field errors are formatted as "FieldName: message" in the Errors list for API responses

License

This project is licensed under the MIT License.

Copyright (c) 2024-2026 Repletory.


For complete documentation, infrastructure setup, and configuration reference, see the RepletoryLib main repository.

Product Compatible and additional computed target framework versions.
.NET 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.
  • net10.0

    • No dependencies.

NuGet packages (34)

Showing the top 5 NuGet packages that depend on RepletoryLib.Common:

Package Downloads
RepletoryLib.Caching.Abstractions

Caching abstractions and interfaces for RepletoryLib

RepletoryLib.Messaging.Abstractions

Messaging abstractions for RepletoryLib

RepletoryLib.Security.Encryption

Encryption, hashing, and data masking services for RepletoryLib

RepletoryLib.Communication.Abstractions

Communication abstractions and interfaces for email, SMS, WhatsApp, and push notifications

RepletoryLib.Tracing

OpenTelemetry distributed tracing for RepletoryLib

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 323 3/2/2026