Codebelt

Savvyio.Extensions.EFCore

EF Core-backed Savvy I/O data sources, repositories, and data stores built from configurable DbContext callbacks.

.NET 10.0 / .NET 9.0 MIT v5.0.8 13,764 downloads

Overview

Savvyio.Extensions.EFCore adapts the core Savvy I/O persistence abstractions to Microsoft Entity Framework Core. It gives you a configurable DbContext, an EF Core-backed data source and unit of work, a repository implementation for entities that expose IIdentity<TKey>, and a DTO-oriented data store that queries through DbSet<T>.

This package sits below Savvyio.Extensions.DependencyInjection.EFCore. Use it when you want the EF Core-backed building blocks directly and can configure the underlying DbContext yourself through EfCoreDataSourceOptions.

Key APIs

EfCoreDataSource is the package's concrete IEfCoreDataSource implementation. Its public constructor creates an EfCoreDbContext from EfCoreDataSourceOptions, Set<TEntity>() exposes the underlying DbSet<TEntity>, and SaveChangesAsync commits the current EF Core unit of work through DbContext.SaveChangesAsync.

IEfCoreDataSource is the boundary that combines Savvy I/O data source and unit of work behavior with EF Core set access. Consumers that depend on this interface can request Set<TEntity>() without depending directly on a concrete DbContext type.

EfCoreDataSourceOptions defines the three configuration delegates that shape the context lifecycle. ContextConfigurator configures the DbContextOptionsBuilder, ConventionsConfigurator participates in EF Core convention setup, and ModelConstructor customizes model building.

EfCoreDbContext is the package's prebuilt DbContext wrapper. It stores the supplied EfCoreDataSourceOptions in its Options property and forwards EF Core lifecycle callbacks to the configured delegates in OnConfiguring, ConfigureConventions, and OnModelCreating.

EfCoreRepository<TEntity, TKey> provides the EF Core implementation of IPersistentRepository<TEntity, TKey> for entity types constrained to class, IIdentity<TKey>. Add, AddRange, Remove, and RemoveRange stage changes in the tracked DbSet<TEntity>, while GetByIdAsync and FindAllAsync read entities back through EF Core queries.

EfCoreDataStore<T> provides a DTO-style persistence abstraction over EF Core for any reference type. CreateAsync, UpdateAsync, and DeleteAsync save immediately through the associated IUnitOfWork, while FindAllAsync accepts EfCoreQueryOptions<T> so callers can either fetch all rows or filter them with a predicate.

EfCoreQueryOptions<T> extends AsyncOptions with a single Predicate property. EfCoreDataStore<T>.FindAllAsync uses that expression when present and otherwise returns every row for the mapped type.

Basic usage

using System;
using System.Threading.Tasks;
using Codebelt.Extensions.Xunit;
using Microsoft.EntityFrameworkCore;
using Savvyio.Extensions.EFCore;
using Xunit;

namespace MyProject.Tests;

public class EfCoreDataSourceTests : Test
{
    public EfCoreDataSourceTests(ITestOutputHelper output) : base(output) { }

    [Fact]
    public async Task SaveChanges_MissingProvider_ThrowsInvalidOperationException()
    {
        var options = new EfCoreDataSourceOptions
        {
            ContextConfigurator = builder => builder.EnableSensitiveDataLogging(),
            ModelConstructor = _ => { },
            ConventionsConfigurator = _ => { }
        };

        using var source = new EfCoreDataSource(options);
        var context = Assert.IsType<EfCoreDbContext>(source.DbContext);

        TestOutput.WriteLine($"Context type: {context.GetType().Name}");

        Assert.Same(options, context.Options);

        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => source.SaveChangesAsync());

        TestOutput.WriteLine(ex.Message);
        Assert.StartsWith("No database provider has been configured for this DbContext.", ex.Message);
    }
}

Use this pattern when you want to construct the EF Core-backed Savvy I/O data source directly and verify that provider configuration is part of your startup contract. It matters because the package supplies the Savvy I/O abstractions and context hooks, but it deliberately leaves the actual EF Core provider choice to your ContextConfigurator.

Installation

dotnet add package Savvyio.Extensions.EFCore

Usage guidance

Choose this package when your application already uses Savvy I/O repositories, data stores, or unit-of-work patterns and EF Core is the persistence engine you want underneath them. If you want container registrations as well, Savvyio.Extensions.DependencyInjection.EFCore is the better fit, and if you need domain-specific aggregate or event-sourcing support, the EF Core domain packages are more appropriate than this lower-level foundation.

Family packages