RepletoryLib.Common
1.0.0
dotnet add package RepletoryLib.Common --version 1.0.0
NuGet\Install-Package RepletoryLib.Common -Version 1.0.0
<PackageReference Include="RepletoryLib.Common" Version="1.0.0" />
<PackageVersion Include="RepletoryLib.Common" Version="1.0.0" />
<PackageReference Include="RepletoryLib.Common" />
paket add RepletoryLib.Common --version 1.0.0
#r "nuget: RepletoryLib.Common, 1.0.0"
#:package RepletoryLib.Common@1.0.0
#addin nuget:?package=RepletoryLib.Common&version=1.0.0
#tool nuget:?package=RepletoryLib.Common&version=1.0.0
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.
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>andResultfor explicit success/failure flows without exceptions - BaseEntity -- Abstract base class with identity, audit fields, soft-delete, and multi-tenancy
- Standardized exceptions --
AppExceptionandValidationExceptionwith HTTP status codes - Error response model --
ErrorResponsefor consistent API error payloads - Pagination --
PagedRequest,PagedResult<T>for offset-based pagination - Extension methods -- String, DateTime, and Guid helpers
- Service interfaces --
ICurrentUserServiceandICurrentTenantServicecontracts
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 | Versions 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. |
-
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 |