PANiXiDA.Core.Infrastructure.Persistence.Ef
1.0.2
dotnet add package PANiXiDA.Core.Infrastructure.Persistence.Ef --version 1.0.2
NuGet\Install-Package PANiXiDA.Core.Infrastructure.Persistence.Ef -Version 1.0.2
<PackageReference Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" Version="1.0.2" />
<PackageVersion Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" Version="1.0.2" />
<PackageReference Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" />
paket add PANiXiDA.Core.Infrastructure.Persistence.Ef --version 1.0.2
#r "nuget: PANiXiDA.Core.Infrastructure.Persistence.Ef, 1.0.2"
#:package PANiXiDA.Core.Infrastructure.Persistence.Ef@1.0.2
#addin nuget:?package=PANiXiDA.Core.Infrastructure.Persistence.Ef&version=1.0.2
#tool nuget:?package=PANiXiDA.Core.Infrastructure.Persistence.Ef&version=1.0.2
PANiXiDA.Core.Infrastructure.Persistence.Ef
PANiXiDA.Core.Infrastructure.Persistence.Ef is a .NET library that provides Entity Framework Core persistence infrastructure for PANiXiDA Core applications.
It is designed for application and infrastructure packages that use PANiXiDA.Core.Application persistence abstractions, PostgreSQL, DDD aggregate roots, and read models.
Status
Overview
The package bridges PANiXiDA application-layer persistence contracts with EF Core. It provides base write and read DbContexts, repository base classes, a unit of work implementation, audit shadow properties, soft-delete behavior, PostgreSQL DI registration, and helpers for read-model sorting and pagination.
The library is intentionally infrastructure-focused. Domain model design, command/query handlers, and concrete repositories stay in consuming applications.
Features
- PostgreSQL registration extensions for write/read EF Core infrastructure.
WriteDbContext<TDbContext>with HiLo configuration, optional schema naming, assembly configuration scanning, and plural table names.ReadDbContext<TDbContext>with no-tracking queries, automatic read model registration, optional schema naming, and migration exclusion for read models.- Base
EfWriteRepository<TDbContext, TId, TAggregateRoot>integrated withIAggregateTracker. EfUnitOfWork<TDbContext>implementation for save changes and transaction boundaries.- Auditable entity configuration with
CreatedAt,UpdatedAt, andDeletedAtshadow properties. - SaveChanges interceptor that updates audit values and converts deletes with
DeletedAtinto soft deletes. - Read repository helpers for page-based pagination, cursor pagination, dynamic sorting, and projection through
IReadModelMapper.
Quick Start
Requirements
- .NET 10 SDK
- PostgreSQL when using the built-in DI registration methods
- Docker for local integration tests because they use Testcontainers with PostgreSQL
Installation
dotnet add package PANiXiDA.Core.Infrastructure.Persistence.Ef
Configuration
The built-in PostgreSQL registration methods read the connection string named PostgreSqlConnectionString.
{
"ConnectionStrings": {
"PostgreSqlConnectionString": "Host=localhost;Port=5432;Database=panixida;Username=postgres;Password=postgres"
}
}
Register EF Infrastructure
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.DbContexts;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.DependencyInjection;
public static class PersistenceRegistration
{
public static IServiceCollection AddPersistence(
IServiceCollection services,
IConfiguration configuration)
{
return services.AddPostgreSqlEfRepository<AppWriteDbContext, AppReadDbContext>(
configuration);
}
}
public sealed class AppWriteDbContext(
DbContextOptions<AppWriteDbContext> options,
IEnumerable<IInterceptor> interceptors)
: WriteDbContext<AppWriteDbContext>(options, interceptors)
{
}
public sealed class AppReadDbContext(
DbContextOptions<AppReadDbContext> options)
: ReadDbContext<AppReadDbContext>(options)
{
}
Use AddPostgreSqlWriteEfRepository<TWriteDbContext> when the application only needs write-side infrastructure, or AddPostgreSqlReadEfRepository<TReadDbContext> when it only needs read-side infrastructure.
Usage
Write Repository
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using PANiXiDA.Core.Application.Persistence;
using PANiXiDA.Core.Domain.AggregateRoots;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.Write;
public sealed class Order(Guid id) : AggregateRoot<Guid>(id)
{
public string Number { get; private set; } = string.Empty;
}
public sealed class OrderConfiguration : AuditableEntityConfiguration<Order>
{
protected override void ConfigureEntity(EntityTypeBuilder<Order> builder)
{
builder.HasKey(order => order.Id);
builder.Property(order => order.Number).HasMaxLength(64).IsRequired();
}
}
public sealed class OrderRepository(
AppWriteDbContext dbContext,
IAggregateTracker aggregateTracker)
: EfWriteRepository<AppWriteDbContext, Guid, Order>(dbContext, aggregateTracker)
{
}
EfWriteRepository marks aggregate roots for insert, update, or delete and tracks touched aggregate roots through IAggregateTracker. Persistence is completed by IUnitOfWork.SaveChangesAsync or by the transaction pipeline used by the consuming application.
Read Models
using PANiXiDA.Core.Infrastructure.Persistence.Ef.Read;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.Read.Models;
public sealed class OrderReadDbModel : AuditableReadDbModel<Guid>
{
public string Number { get; set; } = string.Empty;
}
public sealed record OrderReadModel(Guid Id, string Number);
public sealed class OrderReadModelMapper
: IReadModelMapper<Guid, OrderReadDbModel, OrderReadModel>
{
public static IQueryable<OrderReadModel> ProjectTo(IQueryable<OrderReadDbModel> query)
{
return query.Select(order => new OrderReadModel(order.Id, order.Number));
}
}
Concrete ReadDbModel<TId> types in the read DbContext assembly are registered automatically. By default they are mapped as no-tracking models and excluded from migrations, which is useful when read models point to tables or views owned by another context.
Read Repository
using PANiXiDA.Core.Application.Querying.Pagination;
using PANiXiDA.Core.Application.Querying.Sorting;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.Read;
public sealed class OrderReadRepository(AppReadDbContext dbContext)
: EfReadRepository<AppReadDbContext, Guid, OrderReadDbModel>(dbContext)
{
public Task<OrderReadModel?> GetByIdAsync(Guid id, CancellationToken cancellationToken)
{
return GetByIdAsync<OrderReadModel, OrderReadModelMapper>(id, cancellationToken);
}
public Task<PaginationResult<OrderReadModel>> GetPageAsync(
PaginationParameters pagination,
SortParameters sort,
CancellationToken cancellationToken)
{
return GetPagedResultAsync<OrderReadModel, OrderReadModelMapper>(
Query,
pagination,
sort,
cancellationToken);
}
}
Behavior Notes
- Audit timestamps are stored as EF Core shadow properties for write entities configured through
AuditableEntityConfiguration<TEntity>. - Added entities receive
CreatedAtandUpdatedAt. - Modified entities receive a new
UpdatedAt;CreatedAtis marked as not modified. - Deleted entities that have
DeletedAtare converted to modified entities and receiveDeletedAtandUpdatedAt. AuditableReadDbModel<TId>and auditable write configurations apply a query filter that hides rows whereDeletedAtis not null.EfReadRepositoryuses dynamic sorting field names; callers should pass known model property names, not arbitrary user input without validation.
Project Structure
.
|-- src/
| `-- PANiXiDA.Core.Infrastructure.Persistence.Ef/
|-- tests/
| |-- PANiXiDA.Core.Infrastructure.Persistence.Ef.IntegrationTests/
| `-- PANiXiDA.Core.Infrastructure.Persistence.Ef.UnitTests/
|-- .github/workflows/ci.yml
|-- Directory.Build.props
|-- Directory.Build.targets
|-- Directory.Packages.props
|-- global.json
|-- version.json
|-- LICENSE
`-- README.md
Development
Build
dotnet restore
dotnet build --configuration Release
Format
dotnet format
Test
dotnet test --configuration Release
Integration tests start a PostgreSQL container through Testcontainers. Docker must be running before executing the full test suite.
To run only unit tests:
dotnet test tests/PANiXiDA.Core.Infrastructure.Persistence.Ef.UnitTests/PANiXiDA.Core.Infrastructure.Persistence.Ef.UnitTests.csproj --configuration Release
To run only integration tests:
dotnet test tests/PANiXiDA.Core.Infrastructure.Persistence.Ef.IntegrationTests/PANiXiDA.Core.Infrastructure.Persistence.Ef.IntegrationTests.csproj --configuration Release
Test With Coverage
dotnet test --configuration Release --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml
Pack
dotnet pack --configuration Release
Full Local Validation
dotnet restore
dotnet format
dotnet build --configuration Release
dotnet test --configuration Release
dotnet pack --configuration Release
Tooling and Conventions
This repository uses:
- .NET 10
- Nullable enabled
- Implicit usings enabled
- Central package management
- Microsoft Testing Platform
- xUnit v3
- FluentAssertions
- Testcontainers for PostgreSQL integration tests
- Nerdbank.GitVersioning
License
This project is licensed under the Apache-2.0 license.
See the LICENSE file for details.
Maintainers
Maintained by PANiXiDA.
For questions or improvements, use GitHub Issues or Pull Requests.
| 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
- EFCore.NamingConventions (>= 10.0.1)
- Humanizer.Core (>= 3.0.10)
- Microsoft.EntityFrameworkCore (>= 10.0.5)
- Npgsql (>= 10.0.2)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.1)
- PANiXiDA.Core.Application (>= 1.0.2)
- System.Linq.Dynamic.Core (>= 1.7.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.2 | 49 | 5/18/2026 |