Sumapap.Persistence
1.0.1
dotnet add package Sumapap.Persistence --version 1.0.1
NuGet\Install-Package Sumapap.Persistence -Version 1.0.1
<PackageReference Include="Sumapap.Persistence" Version="1.0.1" />
<PackageVersion Include="Sumapap.Persistence" Version="1.0.1" />
<PackageReference Include="Sumapap.Persistence" />
paket add Sumapap.Persistence --version 1.0.1
#r "nuget: Sumapap.Persistence, 1.0.1"
#:package Sumapap.Persistence@1.0.1
#addin nuget:?package=Sumapap.Persistence&version=1.0.1
#tool nuget:?package=Sumapap.Persistence&version=1.0.1
Sumapap.Persistence
๐ก Overview
Sumapap.Persistence provides a thin, opinionated set of persistence abstractions and helper utilities intended to simplify implementing repositories, specifications and unit-of-work patterns across different data access technologies (EF Core, Dapper, etc.). The package focuses on:
- Common repository contracts (read, write, read/write)
- Unit of Work abstraction for transactional scope
- Specification pattern helpers (filtering, includes, paging)
- DI helpers for registering repository implementations easily
The goal is to let your domain and application layers depend on a consistent persistence surface while keeping concrete implementations swappable.
โจ Why use Sumapap.Persistence?
- Provides clear separation between domain and data access layers via small interfaces.
- Encourages use of the Specification pattern to centralize query logic.
- Standardizes repository and unit-of-work surface across different persistence engines.
- Includes convenience DI helpers to register repository implementations with correct lifetimes and service mappings.
๐ Quick start
- Add the package to your project (when published on NuGet):
dotnet add package Sumapap.Persistence
- Implement your entity (must implement
IEntityorIEntity<TKey>):
public class Order : IEntity<Guid>
{
public Guid Id { get; set; }
}
- Implement a repository for your persistence technology (read/write):
public class EfOrderRepository : IReadWriteRepository<Order>
{
// implement methods using your DbContext
}
- Register repository in DI using provided helpers:
// when implementing EfOrderRepository as concrete implementation
services.AddScopedRepository<EfOrderRepository, Order>();
// or if you have an interface abstraction IOrderRepository
services.AddScopedRepository<IOrderRepository, EfOrderRepository, Order>();
- Use repository and unit of work in your services:
var repo = unitOfWork.GetRepository<Order>();
var orders = await repo.GetAllAsync();
await unitOfWork.SaveChangesAsync();
๐ Features and usage
Repository interfaces
IRepository/IRepository<TEntity>โ marker base interfaces.IReadRepository<TEntity>โ rich read-only API supporting synchronous and asynchronous queries, streaming, specification-based queries and paging viaIQuery.IWriteRepository<TEntity>โ mutation API (Add,Update,Delete) with synchronous and asynchronous variants and Save/SaveAsync.IReadWriteRepository<TEntity>โ combination of read and write APIs (not explicitly shown in code but used as returned type inIUnitOfWork).
Common patterns:
- Use
Find,FirstOrDefault,SingleOrDefault,WhereorGetAllfor synchronous reads. - Prefer the async variants in application code (e.g.,
GetAllAsync,FirstOrDefaultAsync). - Use streaming (
IAsyncEnumerable<T>) for large result sets to reduce memory pressure.
Unit of Work
IUnitOfWorkprovides scoped coordination across repositories and transactional control methods (BeginTransactionAsync,CommitTransactionAsync,RollbackTransactionAsync).- Use
GetRepository<TEntity>()to obtain a repository instance that participates in the unit of work. - Call
SaveChangesAsync()to persist changes and optionally control transactions explicitly when needed.
Example:
await using var uow = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>();
uow.ExecuteAsync(c =>
{
var repo = uow.GetRepository<Order>();
repo.Add(newOrder);
}, cancellationToken);
Specification pattern
ISpecification<T>encapsulates query criteria (Expression<Func<T,bool>>? Criteria), includes (list of navigation paths) and optionalIQuery(paging and sorting options).BaseSpecification<T>provides helpers for building specifications and storing includes and query options.IncludeSpecification<T>is a simple specialization to specify only Includes (or includes+criteria).PagingSpecification<T>is a helper that sets paging (Offset or Cursor) and sorting viaIQuerywrappers.
Usage example:
var spec = new PagingSpecification<Order>(o => o.CustomerId == customerId, new OffsetPaginationOptions(0, 20));
var page = await repo.GetAllAsync(spec);
Dependency injection helpers
DependencyInjectionexposes extension methods to register repository implementations with correct service mappings and lifetimes:AddScopedRepository<TImpl, TEntity>()โ registers implementation and maps it toIReadRepository<TEntity>,IWriteRepository<TEntity>,IReadWriteRepository<TEntity>andIRepository<TEntity>when applicable.AddScopedRepository<TService, TImpl, TEntity>()โ same as above plus registers a service abstraction.- Equivalent
AddTransientRepositoryoverloads for transient lifetime.
This helps avoid repetitive registration code and ensures repositories are available via multiple abstraction types.
โ ๏ธ Notes & best practices
- Prefer the async APIs throughout your application to avoid thread starvation in server scenarios.
- Keep specifications focused and composable. Specifications should describe the "what" (filter, includes, query options) and not the persistence mechanism.
- Dispatch domain events (if using) only after the unit-of-work completes to avoid notifying stakeholders about rolled-back changes.
- Decide repository lifetime (Scoped vs Transient) based on your persistence technology (
EF Core: Scoped;Dapper: typically Transient). - Implement concrete repositories to honor the contracts โ e.g.,
DetatchFromTrackingshould detach entities when usingEF Coreto prevent unintended state tracking.
Example
// Specification
public class OrdersByCustomerSpec : BaseSpecification<Order>
{
public OrdersByCustomerSpec(Guid customerId)
: base(o => o.CustomerId == customerId)
{
AddInclude("OrderItems.Product");
}
}
// Registration
services.AddScopedRepository<IOrderRepository, EfOrderRepository, Order>();
// Usage in app service
var repo = unitOfWork.GetRepository<Order>();
var orders = await repo.GetAllAsync(new OrdersByCustomerSpec(customerId));
await unitOfWork.SaveChangesAsync();
๐ช Contributions
Contributions are welcome! If you encounter a bug, have a suggestion, or want to contribute code, please follow these steps:
- Check the GitHub Issues to see if your issue or idea has already been reported.
- If not, open a new issue to describe the bug or feature request.
- For code contributions:
- Fork the Project repository.
- Create your Feature Branch (
git checkout -b feature/YourAmazingFeature). - Commit your Changes. Adhere to conventional commit messages if possible.
- Push to the Branch and open a Pull Request against
main.
- Please try to follow the existing coding style and include unit tests for new or modified functionality.
โญ License
Distributed under the MIT License. See the LICENSE file in the repository for more information.
๐ฉ Contact
GitHub @muhirwanto-dev
Project Url https://github.com/muhirwanto-dev/sumapap/tree/main/source/Sumapap.Persistence
๐ช Support
If you like this project and want to support it, you can buy me a coffee๏ธ. Your coffee will keep me awake while developing this project โ.
<br /> <div align="center"> <a href="https://buymeacoffee.com/muhirwanto.dev"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=muhirwanto.dev&button_colour=FFDD00&font_colour=000000&font_family=Comic&outline_colour=000000&coffee_colour=ffffff" /></a> </div>
| 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
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- Sumapap.Queries (>= 1.3.2)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Sumapap.Persistence:
| Package | Downloads |
|---|---|
|
Sumapap.Persistence.EfCore
`Sumapap.Persistence.EfCore` implements ReadWriteRepository and Unit of Work abstraction using `EntityFrameworkCore`. |
GitHub repositories
This package is not used by any popular GitHub repositories.