MonadicSharp.Persistence 1.0.0

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

MonadicSharp.Persistence

Result-aware repository and Unit of Work for EF Core 8 — NotFound, Conflict, and DatabaseError are typed Result<T> values, never null or unhandled exceptions.

NuGet .NET


Overview

MonadicSharp.Persistence brings Railway-Oriented Programming to the data access layer:

  • IRepository<T, TId> — full read-write repository, all methods return Result<T>
  • IReadRepository<T, TId> — read-only projection for CQRS-style separation
  • IUnitOfWorkSaveChangesAsync returns Result<int>, concurrency conflicts are typed errors
  • EfCoreRepository<T, TId> — EF Core 8 implementation
  • EfCoreUnitOfWork — supports transactions via BeginTransactionAsync / CommitTransactionAsync
  • QueryableExtensions.ToResultAsync(), .FirstOrDefaultResultAsync(), .SingleResultAsync() for inline LINQ
  • PersistenceError — typed error factory for all persistence failure scenarios

Installation

dotnet add package MonadicSharp.Persistence
dotnet add package Microsoft.EntityFrameworkCore.SqlServer  # or Npgsql, Sqlite, etc.

Quick Start

1. Define your DbContext as usual

public class AppDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();
    // ...
}

2. Register in DI

services.AddDbContext<AppDbContext>(opts =>
    opts.UseNpgsql(connectionString));

services
    .AddMonadicSharpPersistence<AppDbContext>()          // registers IUnitOfWork
    .AddMonadicSharpRepository<Order, Guid>();            // registers IRepository<Order, Guid>

3. Use in your services

public class OrderService(IRepository<Order, Guid> orders, IUnitOfWork uow)
{
    public async Task<Result<Order>> PlaceOrderAsync(CreateOrderDto dto)
    {
        var order = new Order(dto.CustomerId, dto.Items);

        var addResult = await orders.AddAsync(order);
        if (addResult.IsFailure) return addResult;

        var saveResult = await uow.SaveChangesAsync();
        return saveResult.IsSuccess
            ? Result<Order>.Success(order)
            : Result<Order>.Failure(saveResult.Error);
    }

    public async Task<Result<Order>> GetOrderAsync(Guid id) =>
        await orders.FindAsync(id);
    // Returns PersistenceError.NotFound if missing — no null checks needed
}

IRepository API

// Read
Task<Result<T>>                 FindAsync(TId id, ct)
Task<Result<T>>                 FirstOrDefaultAsync(predicate, ct)
Task<Result<IReadOnlyList<T>>>  ListAsync(predicate?, ct)
Task<Result<bool>>              AnyAsync(predicate, ct)
Task<Result<int>>               CountAsync(predicate?, ct)

// Write (changes not persisted until SaveChangesAsync)
Task<Result<T>>     AddAsync(T entity, ct)
Task<Result<Unit>>  AddRangeAsync(IEnumerable<T> entities, ct)
Result<T>           Update(T entity)
Task<Result<Unit>>  DeleteAsync(TId id, ct)
Result<Unit>        Delete(T entity)

IUnitOfWork API

Task<Result<int>>   SaveChangesAsync(ct)             // persists all pending changes
Task<Result<Unit>>  BeginTransactionAsync(ct)
Task<Result<Unit>>  CommitTransactionAsync(ct)
Task<Result<Unit>>  RollbackTransactionAsync(ct)

Transaction example

await uow.BeginTransactionAsync();

var addOrder = await orders.AddAsync(order);
var debitStock = await inventory.UpdateAsync(item);

if (addOrder.IsFailure || debitStock.IsFailure)
{
    await uow.RollbackTransactionAsync();
    return Result<Unit>.Failure(addOrder.Error ?? debitStock.Error!);
}

await uow.SaveChangesAsync();
await uow.CommitTransactionAsync();

QueryableExtensions

For custom queries that go beyond the repository interface:

var recentOrders = await dbContext.Orders
    .Where(o => o.CreatedAt > DateTime.UtcNow.AddDays(-7))
    .OrderByDescending(o => o.CreatedAt)
    .ToResultAsync(ct);

// Single entity
var order = await dbContext.Orders
    .SingleResultAsync(o => o.ExternalRef == externalId, ct);
// Returns PersistenceError.NotFound or PersistenceError.TooManyResults

// First or not-found
var latest = await dbContext.Orders
    .OrderByDescending(o => o.CreatedAt)
    .FirstOrDefaultResultAsync(ct);

Error Codes

Code Meaning
PERSISTENCE_NOT_FOUND Entity with the given id/predicate does not exist
PERSISTENCE_CONFLICT Entity with the same key already exists
PERSISTENCE_CONCURRENCY_CONFLICT DbUpdateConcurrencyException — row was modified by another process
PERSISTENCE_DATABASE_ERROR Unexpected database exception
PERSISTENCE_TOO_MANY_RESULTS SingleResultAsync found more than one match
PERSISTENCE_TRANSACTION_ALREADY_ACTIVE BeginTransactionAsync called twice
PERSISTENCE_NO_ACTIVE_TRANSACTION Commit/Rollback without Begin

License

MIT — part of MonadicSharp.Framework.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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 MonadicSharp.Persistence:

Package Downloads
MonadicSharp.Framework

Meta-package for the MonadicSharp Framework — install this single package to get Agents, Caching, Http, Persistence, Security, and Telemetry in one shot. For à-la-carte usage, reference individual MonadicSharp.* packages instead.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 39 3/3/2026