Sumapap.Persistence 1.0.1

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

Sumapap.Persistence

NuGet Version NuGet Downloads License GitHub Issues GitHub Stars GitHub Forks Contributions Welcome

๐Ÿ’ก 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

  1. Add the package to your project (when published on NuGet):
 dotnet add package Sumapap.Persistence
  1. Implement your entity (must implement IEntity or IEntity<TKey>):
public class Order : IEntity<Guid>
{
    public Guid Id { get; set; }
}
  1. Implement a repository for your persistence technology (read/write):
public class EfOrderRepository : IReadWriteRepository<Order>
{
    // implement methods using your DbContext
}
  1. 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>();
  1. 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 via IQuery.
  • 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 in IUnitOfWork).

Common patterns:

  • Use Find, FirstOrDefault, SingleOrDefault, Where or GetAll for 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

  • IUnitOfWork provides 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 optional IQuery (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 via IQuery wrappers.

Usage example:

var spec = new PagingSpecification<Order>(o => o.CustomerId == customerId, new OffsetPaginationOptions(0, 20));
var page = await repo.GetAllAsync(spec);

Dependency injection helpers

  • DependencyInjection exposes extension methods to register repository implementations with correct service mappings and lifetimes:
    • AddScopedRepository<TImpl, TEntity>() โ€” registers implementation and maps it to IReadRepository<TEntity>, IWriteRepository<TEntity>, IReadWriteRepository<TEntity> and IRepository<TEntity> when applicable.
    • AddScopedRepository<TService, TImpl, TEntity>() โ€” same as above plus registers a service abstraction.
    • Equivalent AddTransientRepository overloads 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., DetatchFromTracking should detach entities when using EF Core to 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:

  1. Check the GitHub Issues to see if your issue or idea has already been reported.
  2. If not, open a new issue to describe the bug or feature request.
  3. 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.
  4. 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 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.

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.

Version Downloads Last Updated
1.0.1 83 4/4/2026
1.0.0 84 4/4/2026