RA.Utilities.Data.EntityFramework
Version 10.2.1
Maintenance release updating Entity Framework Core to the latest .NET 10 servicing patch. No API or behavior changes.
📝 Improvements
- Microsoft.EntityFrameworkCore 10.0.12 → 10.0.13.
Version 10.2.0
This release adds IbanConverter, so the IBAN value object introduced in RA.Utilities.Core.ValueObjects 10.2.0 can be persisted alongside the other value objects. The release is additive: no existing type, signature, or registration changed.
✨ New Features
IbanConverter: AValueConverter<IBAN, string>inRA.Utilities.Data.EntityFramework.Conventions. It writes the normalizedValue(for exampleGB82WEST12345698765432) and reads throughnew IBAN(value), so structural and ISO 7064 mod-97 validation run on materialization.- Model-wide configuration: The converter works with
configurationBuilder.Properties<IBAN>().HaveConversion<IbanConverter>()as well as per-propertyHasConversion(new IbanConverter()).
public sealed class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.Property(c => c.Iban).HasConversion(new IbanConverter()).HasMaxLength(34);
}
}
📝 Notes
- Configure the column length explicitly. An IBAN has at most 34 characters; relying on the provider default (
nvarchar(max)on SQL Server) works but is imprecise. - Because the converter validates on read, rows that already contain malformed IBANs throw
BadRequestExceptionwhen materialized. Clean or migrate the data before enabling the converter. - The converter writes the canonical form, so
gb82 west 1234 5698 7654 32andGB82WEST12345698765432persist identically.
📦 Dependencies
RA.Utilities.Core.ValueObjects10.2.0 — addsIBAN.RA.Utilities.Data.Abstractions10.0.5.RA.Utilities.Data.Entities10.1.2 (transitive via the abstractions).Microsoft.EntityFrameworkCore10.0.12.
⚠️ Breaking Changes
None. Existing repositories, converters, registrations, and model configurations are unchanged.
Version 10.1.0
This release adds persistence support for the value objects of RA.Utilities.Core.ValueObjects — EF Core value converters and a HasMoney convention that stores a Money in two database columns — renames DependencyInjectionExtensions to ServiceCollectionExtensions, and refactors the extension classes to C# 14 extension members.
✨ New Features
- Added
MoneyConventionExtensions.HasMoney: Maps aMoneyproperty to two separate database columns —Amountin a decimal column andCurrencyin a string column (throughCurrencyConverter) — instead of storing the whole value in a single column.- EF Core value converters map a property to a single column, so
HasMoneymaps the property as an EF Core complex property instead. Columns are named after the property, e.g.Balance_AmountandBalance_Currency; theAmountcolumn uses the provider default fordecimal(decimal(18,2)on SQL Server,TEXTon SQLite). - Supports required (
Money), optional (Money?, stored asNULLin both columns) and multipleMoneyproperties on the same entity. BecauseMoneyis immutable, updates persist by replacing the instance on a tracked entity. - The existing
MoneyConverter(single JSON string column) is unchanged and remains available for single-column mappings.
- EF Core value converters map a property to a single column, so
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Wallet>().HasMoney(w => w.Balance);
}
⚠️ Breaking Changes
DependencyInjectionExtensionsrenamed toServiceCollectionExtensions: The class providingAddRepositoryBase(),AddReadRepositoryBase()andAddWriteRepositoryBase()now follows the ASP.NET Core naming convention. The namespaceRA.Utilities.Data.EntityFramework.Extensionsand all method signatures are unchanged, so extension-method call sites such asbuilder.Services.AddRepositoryBase()keep compiling; only code that references the class by name — atypeof(...)expression, a static call or a reflection string — needs updating.
- var registrationType = typeof(DependencyInjectionExtensions);
+ var registrationType = typeof(ServiceCollectionExtensions);
📝 Improvements
- C# 14 extension members:
ServiceCollectionExtensionsand theHasMoneyconvention inMoneyConventionExtensionsnow declare their methods insideextension(...)blocks. The generated members keep the same names, signatures and namespaces, so existing call sites are unaffected.
📦 Dependencies
- New dependency on
RA.Utilities.Core.ValueObjects10.1.0.
Version 10.0.2
This release makes all repository implementations generic over the primary key type (TKey), aligns deletion with the unit-of-work flow, and fixes automatic modification timestamps for mutable entity types.
⚠️ Breaking Changes
- Repository classes and interfaces are now generic over the primary key type (
TKey):RepositoryBase<T>,ReadRepositoryBase<T>,WriteRepositoryBase<T>and their interfaces became<T, TKey>, andGetByIdAsync/DeleteAsync/DeleteRangeAsyncnow take aTKeyinstead of aGuid. The entity type parameter is constrained toCoreEntity<TKey>(where T : CoreEntity<TKey> where TKey : notnull), so numeric and string keys are supported alongsideGuid.RA.Utilities.Data.Entities10.1.1 introduces theIEntitymarker interface, implemented byCoreEntity<TKey>.
+ public interface IReadRepositoryBase<T, TKey> : IRepository where T : CoreEntity<TKey> where TKey : notnull
- public interface IReadRepositoryBase<T> : IRepository where T : notnull, CoreEntity
+ Task<T?> GetByIdAsync(TKey id, CancellationToken cancellationToken = default);
- Task<T?> GetByIdAsync<TId>(TId id, CancellationToken cancellationToken = default) where TId : notnull;
🔄 Behavior Changes
- Delete operations are mark-then-save:
DeleteAsyncandDeleteRangeAsyncload matching entities and mark them for deletion in the EF Core change tracker. CallSaveChangesAsyncto persist the deletion. DeleteRangeAsyncresult: The method accepts anIEnumerable<TKey>of keys and returns the number of entities found and marked for deletion.
🐛 Bug Fixes
- Timestamp interceptor: Property detection now uses EF Core metadata, allowing
WriteEntity<TKey>descendants to receiveLastModifiedAtupdates correctly.
📦 Dependencies
RA.Utilities.Data.Abstractions10.0.3 or later.RA.Utilities.Data.Entities10.1.1 or later.
Version 10.0.1
✨ No Breaking Changes
Change generic type constraint from BaseEntity in to CoreEntity in repository interfaces.
+ public interface IReadRepositoryBase<T> where T : notnull, CoreEntity
- public interface IReadRepositoryBase<T> where T : notnull, BaseEntity
Version 10.0.0
Updated package version 10.0.9-rc (release candidate) to 10.0.0, indicating a transition to a stable release.
This change signifies that the project is no longer in the release candidate phase and is considered ready for production use, reflecting confidence in its stability and completeness.
Version 10.0.0-rc.2
This release of RA.Utilities.Data.EntityFramework provides concrete implementations of the Repository and Unit of Work patterns for Entity Framework Core. It serves as the persistence layer for the abstractions defined in RA.Utilities.Data.Abstractions.
✨ Key Features
-
Generic Repository Implementations:
RepositoryBase<T>: A full implementation ofIRepositoryBase<T>for complete CRUD functionality.ReadRepositoryBase<T>: A read-only repository that usesAsNoTracking()by default for efficient querying, ideal for CQS patterns.WriteRepositoryBase<T>: A write-only repository for command operations (Add, Update, Delete).
-
Generic Unit of Work Implementation:
UnitOfWork<TContext>: A generic implementation ofIUnitOfWorkthat manages theDbContextlifecycle and ensures transactional integrity by saving all changes atomically.
-
Dependency Injection Extensions:
- Includes
AddRepositoryBase(),AddReadRepositoryBase(), andAddWriteRepositoryBase()extension methods to simplify the registration of generic repositories in your application's DI container.
- Includes
🚀 Getting Started
- Define Your DbContext: Create your
ApplicationDbContextinheriting fromDbContext.
public class ApplicationDbContext : DbContext, IDbContext
{
// ... DbSets
}
- Register Services: In
Program.cs, register yourDbContext, theUnitOfWork, and your repositories.
// Register DbContext
builder.Services.AddDbContext<ApplicationDbContext>(...);
// Register UnitOfWork and generic repositories
builder.Services.AddScoped<IUnitOfWork, UnitOfWork<ApplicationDbContext>>();
builder.Services.AddRepositoryBase(); // Registers IRepositoryBase<>
- Use in Your Application: Inject
IUnitOfWorkorIRepositoryBase<T>into your services to interact with the database.
public class ProductService(IRepositoryBase<Product> productRepo, IUnitOfWork uow)
{
// ... use repository methods and uow.SaveChangesAsync()
}