All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
BaseEntity<TKey>/BaseEntity— strongly-typed primary key base classAuditableEntity<TKey>/AuditableEntity— addsCreatedAt,CreatedBy,UpdatedAt,UpdatedBySoftDeletableEntity<TKey>/SoftDeletableEntity— addsIsDeleted,DeletedAt,DeletedByFullEntity<TKey>/FullEntity— soft-delete + audit + optimistic concurrency (RowVersion)
BaseEntityConfiguration<TEntity, TKey>— auto-configures primary keyAuditableEntityConfiguration<TEntity, TKey>— adds audit columns andCreatedAtindexSoftDeletableEntityConfiguration<TEntity, TKey>— addsIsDeleteddefault and composite index
IAuditable— audit timestamp contractIFullAuditable— field-level change history contractISoftDeletable— soft delete contractIConcurrencyAware— optimistic concurrency contractIUserProvider— current user resolution contractIRepository<T>/IReadRepository<T>— repository contractsIUnitOfWork— unit of work contractISpecification<T>/ISpecification<T, TResult>— specification pattern contracts
SoftDeleteInterceptor— interceptsDELETEand converts toUPDATE SET IsDeleted = true- Global query filter automatically excludes soft-deleted rows
IncludeDeleted()— bypass the filter to include soft-deleted rowsOnlyDeleted()— query only soft-deleted rowsGetDeletedAsync()— load all soft-deleted rows from aDbSet<T>Restore()— clear soft-delete flags on an entityHardDelete()— permanently remove a record bypassing the interceptor- Optional cascade soft delete for loaded navigation properties
AuditInterceptor— auto-stampsCreatedAt/CreatedByon insert andUpdatedAt/UpdatedByon update- Protects
CreatedAtandCreatedByfrom being overwritten on update - Optional full audit log (
IFullAuditable) — records every field change in anAuditLogtable
Repository<T>— generic implementation ofIRepository<T>andIReadRepository<T>UnitOfWork<TContext>— groups multiple repository operations into a singleSaveChangesAsync- All registered automatically via
AddEfCoreExtensions
Specification<T>base class withAddCriteria,AddInclude,ApplyOrderBy,ApplyPaging,ApplyAsNoTracking,ApplyAsSplitQuerySpecification<T, TResult>— projecting specification withApplySelectorAnd()/Or()combinators — compose specifications at runtimeSpecificationBuilder<T>— fluent inline builder for one-off queriesApplySpecificationextension — apply any spec to anIQueryable<T>ToListAsync(ISpecification<T, TResult>)— project and materialise in one callToPagedFromSpecAsync— apply spec criteria then paginate
ToPagedAsync— offset pagination returningPagedResult<T>with full metadataSelectToPagedAsync— project before materialising, then paginateToKeysetPagedAsync— keyset/cursor pagination returningKeysetPagedResult<T>PagedResult<T>.Map<TDestination>— map items to a different type while preserving metadata
ApplyFilters(FilterDescriptor[])— apply runtime filter arrays to anyIQueryable<T>- Supported operators:
eq,ne,gt,gte,lt,lte,contains,startswith,endswith,isnull,isnotnull,in,between ApplySorts(SortDescriptor[])— apply runtime multi-column sorting- Dot-separated nested property paths supported in both filters and sorts
GetByIdAsync/GetByIdOrThrowAsync— single entity lookup by primary keyGetByIdsAsync— batch lookup translating toWHERE key IN (...)GetAllAsync— load all rows from aDbSet<T>FindAsync— load by predicate or specificationExistsAsync— existence check by key or predicateWhereIf/WhereIfNotNull/WhereIfNotEmpty— conditional filteringOrderByDynamic/ThenByDynamic— sort by property name stringSelectToListAsync/SelectFirstOrDefaultAsync/SelectDistinctAsync— projection helpersWithNoTracking— alias forAsNoTrackingRemoveRangeAsync— load and stage for deletion by predicate
ExecuteInTransactionAsync— wraps work in a transaction respecting EF Core execution strategyDetachAll— clears all tracked entities from the change trackerTruncateAsync<T>— truncates a table by entity type using EF Core metadata
SlowQueryInterceptor— logs a warning for any query exceeding a configurable threshold- Configured via
LogSlowQueries(TimeSpan threshold)
EfCoreException— abstract base for all EfCoreKit exceptionsEntityNotFoundException— thrown byGetByIdOrThrowAsyncandRemoveByIdAsyncConcurrencyConflictException— thrown automatically on stale row-version conflictDuplicateEntityException— throw manually when catching unique constraint violationsInvalidFilterException— thrown byApplyFiltersfor invalid filter descriptors
AddEfCoreExtensions<TContext>— single call registers DbContext, interceptors, repository, and unit of workEfCoreDbContext<TContext>— optional base context that wires interceptors and query filters automatically