Release Notes

10.0.0

Targets .NET 10. Contains breaking changes.

Breaking Changes

Satellite packages folded into Albatross.EFCore

The Albatross.EFCore.Audit, Albatross.EFCore.ChangeReporting and Albatross.EFCore.AutoCacheEviction packages have been removed. Their functionality now lives in the core Albatross.EFCore package, reimplemented as EF Core SaveChangesInterceptor types instead of the old IDbEventHandler pipeline:

Removed package Replacement in Albatross.EFCore
Albatross.EFCore.Audit ChangeAuditInterceptor<TChangeEntity, TEntityId, TActorId>
Albatross.EFCore.ChangeReporting ChangeReportInterceptor<TEntity>
Albatross.EFCore.AutoCacheEviction CacheEvictionInterceptor<T>

Drop the package references and register the interceptors with DbContextOptionsBuilder.AddInterceptors instead.

The old change-reporting configuration surface is gone entirely — IChangeReport, the mutable ChangeReportingOptions class, ChangeReportBuilder<T> and all of its extension methods (ChangeType, FixedHeaders, IgnoreProperties, OnReportGenerated, Prefix, Postfix, Formatter, AsyncFormatter, Format, FormatFixedHeader, NumericFormat, DateFormat, TimeFormat, Deleted, Modified, Added, AllChangeTypes, ExcludeAuditProperties, ExcludeTemporalProperties). ChangeReportInterceptor<TEntity> is configured through required init properties (ChangeType, ShouldSkip, OnReportGenerated) instead.

The audit marker interfaces ICreatedBy, ICreatedUtc, IModifiedBy and IModifiedUtc have been removed. Auditing is now opt-in per entity by implementing IAuditable<TEntityId>.

Albatross.EFCore.CodeGen

The source generator has been rewritten as an incremental generator (IIncrementalGenerator). The previous EntityModelBuilderClassCodeGen and EntityModelClassWalker classes have been replaced by EntityModelBuilderClassCodeGenerator.

The generated CodeGenExtensions class is now emitted into <RootNamespace>.AutoGenerated, falling back to the assembly name. Previously the namespace was taken from whichever DbSession subclass the syntax provider happened to collect first, so a project with more than one session could see the class relocate between builds. Callers whose OnModelCreating is not already in <RootNamespace>.AutoGenerated now need a using for it.

Albatross.Testing.EFCore (removed)

The Albatross.Testing.EFCore package has been removed.


New Features

Albatross.EFCore.CodeAnalysis (new package)

A new Roslyn analyzer package shipping ALBEFCORE001 (MissingTransactionCommit, Usage, Warning). It warns when a transaction created via BeginTransactionAsync is never committed within the method that creates it. A commit is recognized as Commit()/CommitAsync(...) on the transaction, or passing the transaction to SaveAndCommitAsync(...). The search is scoped to the single method body by design.

Albatross.EFCore — Repository Pattern

A new IRepository interface and Repository<T> abstract base class have been added.

  • Add<T>, Delete<T>, Get<T>, GetRequired<T> and SaveChangesAsync
  • GetRequired<T> throws NotFoundException<T> when the key is not found
  • SaveChangesAsync converts constraint violations into semantic exceptions via ISemanticExceptionConverter
  • BeginTransactionAsync and SaveAndCommitAsync own the transaction boundary; SaveAndCommitAsync flushes the change tracker before committing, since CommitAsync alone does not and would otherwise silently drop tracked mutations
  • Repository<T> accepts an ISemanticExceptionConverter for database-specific exception translation

Albatross.EFCore — Change Auditing

ChangeAuditInterceptor<TChangeEntity, TEntityId, TActorId> writes a Change<TEntityId, TActorId> record for every IAuditable<TEntityId> entity modified or deleted in the same transaction. Added entities are intentionally excluded. Changed values are stored as a JSON snapshot; concurrency tokens are skipped.

IAuditable<TEntityId> exposes MaskedProperties and IgnoredProperties. Ignored properties are omitted from the snapshot entirely; masked properties are recorded as ***, proving a change occurred without leaking the value. Both have default implementations returning an empty collection.

Requires IGetCurrentActorId<T> and TimeProvider in DI.

Albatross.EFCore.SqlServer — Constraint Violation Helpers

Two extension methods on Exception have been added:

  • IsUniqueConstraintViolation() — detects SQL errors 2601 and 2627
  • IsForeignKeyConstraintViolation() — detects SQL error 547

Albatross.EFCore.PostgreSQL — Constraint Violation Helpers

Two extension methods on Exception have been added:

  • IsUniqueConstraintViolation() — detects SqlState 23505
  • IsForeignKeyConstraintViolation() — detects SqlState 23503

Semantic exception converters preserve the original exception

EFCoreSemanticExceptionConverter, SqlServerSemanticExceptionConverter and PostgresSemanticExceptionConverter now pass the source exception to ConflictException, NotFoundException and PreconditionFailedException, so the provider exception survives as InnerException.


8.1.2

  • Documentation updates
  • Minor internal fixes