![]() |
VOOZH | about |
dotnet add package PANiXiDA.Core.Infrastructure.Persistence.Ef --version 1.0.5
NuGet\Install-Package PANiXiDA.Core.Infrastructure.Persistence.Ef -Version 1.0.5
<PackageReference Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" Version="1.0.5" />
<PackageVersion Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" Version="1.0.5" />Directory.Packages.props
<PackageReference Include="PANiXiDA.Core.Infrastructure.Persistence.Ef" />Project file
paket add PANiXiDA.Core.Infrastructure.Persistence.Ef --version 1.0.5
#r "nuget: PANiXiDA.Core.Infrastructure.Persistence.Ef, 1.0.5"
#:package PANiXiDA.Core.Infrastructure.Persistence.Ef@1.0.5
#addin nuget:?package=PANiXiDA.Core.Infrastructure.Persistence.Ef&version=1.0.5Install as a Cake Addin
#tool nuget:?package=PANiXiDA.Core.Infrastructure.Persistence.Ef&version=1.0.5Install as a Cake Tool
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.
👁 CI
👁 NuGet
👁 NuGet downloads
👁 Target Framework
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.
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.EfRepository<TDbContext, TId, TAggregateRoot> with async persistence operations integrated with IAggregateTracker.AggregateTracker implementation for tracking touched aggregate roots independently of EF Core.EfUnitOfWork<TDbContext> implementation for transaction boundaries.CreatedAt, UpdatedAt, and DeletedAt shadow properties.DeletedAt into soft deletes.IReadModelMapper.dotnet add package PANiXiDA.Core.Infrastructure.Persistence.Ef
The built-in PostgreSQL registration methods read the connection string named PostgreSqlConnectionString.
{
"ConnectionStrings": {
"PostgreSqlConnectionString": "Host=localhost;Port=5432;Database=panixida;Username=postgres;Password=postgres"
}
}
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.
The registration methods scan DbContext assemblies and register concrete repository implementations as scoped services for non-generic application contracts derived from IRepository<TId, TAggregateRoot> or IReadRepository<TId>.
Write repository implementations are discovered from the write DbContext assembly, and read repository implementations are discovered from the read DbContext assembly.
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using PANiXiDA.Core.Application.Persistence;
using PANiXiDA.Core.Domain.Abstractions;
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 interface IOrderRepository : IRepository<Guid, Order>
{
}
public sealed class OrderRepository(
AppWriteDbContext dbContext,
IAggregateTracker aggregateTracker)
: EfRepository<AppWriteDbContext, Guid, Order>(dbContext, aggregateTracker), IOrderRepository
{
}
EfRepository persists aggregate roots through AddAsync, UpdateAsync, and DeleteAsync, and tracks touched aggregate roots through IAggregateTracker. The built-in write registration adds the generic AggregateTracker implementation as scoped, but consumers can register their own tracker before calling the EF registration methods. When a unit-of-work transaction is active, repository saves participate in that transaction and EfUnitOfWork commits or rolls it back.
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.
using PANiXiDA.Core.Application.Persistence;
using PANiXiDA.Core.Application.Querying.Pagination;
using PANiXiDA.Core.Application.Querying.Sorting;
using PANiXiDA.Core.Infrastructure.Persistence.Ef.Read;
public interface IOrderReadRepository : IReadRepository<Guid>
{
Task<OrderReadModel?> GetByIdAsync(Guid id, CancellationToken cancellationToken);
Task<PaginationResult<OrderReadModel>> GetPageAsync(
PaginationParameters pagination,
SortParameters sort,
CancellationToken cancellationToken);
}
public sealed class OrderReadRepository(AppReadDbContext dbContext)
: EfReadRepository<AppReadDbContext, Guid, OrderReadDbModel>(dbContext), IOrderReadRepository
{
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);
}
}
AuditableEntityConfiguration<TEntity>.CreatedAt and UpdatedAt.UpdatedAt; CreatedAt is marked as not modified.DeletedAt are converted to modified entities and receive DeletedAt and UpdatedAt.AuditableReadDbModel<TId> and auditable write configurations apply a query filter that hides rows where DeletedAt is not null.EfReadRepository uses dynamic sorting field names; callers should pass known model property names, not arbitrary user input without validation.IRepository<TId, TAggregateRoot> or IReadRepository<TId>. Direct base generic repository interfaces are intentionally ignored..
|-- 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
dotnet restore
dotnet build --configuration Release
dotnet format
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
dotnet test --configuration Release --coverage --coverage-output-format cobertura --coverage-output coverage.cobertura.xml
dotnet pack --configuration Release
dotnet restore
dotnet format
dotnet build --configuration Release
dotnet test --configuration Release
dotnet pack --configuration Release
This repository uses:
This project is licensed under the Apache-2.0 license.
See the file for details.
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 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.