A cohesive set of infrastructure libraries for dotnet that utilizes abstractions for event handling, persistence, unit of work, mediator, distributed messaging, event bus, CQRS, email, and more
$ dotnet add package RCommon.PersistencePersistence abstraction layer for RCommon providing the repository pattern, unit of work, specification pattern, and named data store management. This package defines the core interfaces and base classes that ORM-specific implementations (EF Core, Dapper, Linq2Db) build upon.
IQueryable<T> for composable queriesIPaginatedList<T> with built-in orderingInclude / ThenInclude chaining!IsDeleted filtering on reads and logical deletion on writes for entities implementing ISoftDeleteTenantId stamping on writes for entities implementing IMultiTenantAddRCommon()dotnet add package RCommon.Persistence
This package is typically used indirectly through a provider-specific package. However, you program against these abstractions in your application and domain layers:
// Inject repository abstractions into your services
public class OrderService
{
private readonly IGraphRepository<Order> _orderRepo;
private readonly IUnitOfWorkFactory _unitOfWorkFactory;
public OrderService(IGraphRepository<Order> orderRepo, IUnitOfWorkFactory unitOfWorkFactory)
{
_orderRepo = orderRepo;
_unitOfWorkFactory = unitOfWorkFactory;
}
public async Task<Order> GetOrderAsync(int id)
{
return await _orderRepo.FindAsync(id);
}
public async Task<ICollection<Order>> GetPendingOrdersAsync()
{
return await _orderRepo.FindAsync(o => o.Status == OrderStatus.Pending);
}
public async Task<IPaginatedList<Order>> GetOrdersPagedAsync(int page, int pageSize)
{
return await _orderRepo.FindAsync(
o => o.IsActive,
o => o.CreatedDate,
orderByAscending: false,
pageNumber: page,
pageSize: pageSize);
}
public async Task PlaceOrderAsync(Order order)
{
using var unitOfWork = _unitOfWorkFactory.Create(TransactionMode.Default);
await _orderRepo.AddAsync(order);
unitOfWork.Commit();
}
}
Entities implementing ISoftDelete get automatic repository behavior:
!IsDeleted filter is combined with your query expression automaticallyDeleteAsync(entity, isSoftDelete: true) sets IsDeleted = true and performs an UPDATE instead of a DELETEusing RCommon.Entities;
public class Customer : BusinessEntity<int>, ISoftDelete
{
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
// Queries automatically exclude soft-deleted records
var activeCustomers = await repo.FindAsync(c => c.Name.StartsWith("A"));
// Soft delete
await repo.DeleteAsync(customer, isSoftDelete: true);
// Physical delete
await repo.DeleteAsync(customer);Entities implementing IMultiTenant get automatic repository behavior:
TenantId == currentTenantId filter is combined with your query expression automaticallyTenantId is stamped on the entity during AddAsync using the current ITenantIdAccessorusing RCommon.Entities;
public class Product : BusinessEntity<int>, IMultiTenant
{
public string Name { get; set; }
public string? TenantId { get; set; }
}
// Queries automatically scoped to the current tenant
var products = await repo.FindAsync(p => p.Name.Contains("Widget"));
// TenantId is stamped automatically on add
await repo.AddAsync(new Product { Name = "Widget" });When no ITenantIdAccessor is configured (or it returns null), tenant filtering is bypassed entirely.
| Type | Description |
|---|---|
IReadOnlyRepository<TEntity> | Async read operations: find by key, expression, specification, count, and any |
IWriteOnlyRepository<TEntity> | Async write operations: add, add range, update, delete, and delete many |
ILinqRepository<TEntity> | Combines read/write with IQueryable<T> support, pagination, and eager loading |
IGraphRepository<TEntity> | Extends ILinqRepository<T> with change tracking control for full ORMs |
ISqlMapperRepository<TEntity> | Read/write repository for micro-ORMs with explicit table name mapping |
IUnitOfWork | Transaction scope that commits or rolls back on dispose |
IUnitOfWorkFactory | Creates IUnitOfWork instances with configurable transaction mode and isolation level |
IDataStoreFactory | Resolves named data store instances (DbContext, DbConnection, etc.) |
IPersistenceBuilder | Fluent builder interface for registering persistence providers in DI |
LinqRepositoryBase<TEntity> | Abstract base class for LINQ-enabled repository implementations |
SqlRepositoryBase<TEntity> | Abstract base class for SQL mapper repository implementations |
SoftDeleteHelper | Utility for validating ISoftDelete support, marking entities deleted, and building !IsDeleted filter expressions |
MultiTenantHelper | Utility for validating IMultiTenant support, stamping TenantId, and building tenant filter expressions |
For full documentation, visit rcommon.com.
Licensed under the Apache License, Version 2.0.