Overview
Savvyio.Extensions.DapperExtensions adds a concrete Savvy I/O data store for DapperExtensions-backed POCO persistence. It builds on Savvyio.Extensions.Dapper by supplying a DapperDataStore<T, TOptions> implementation that uses DapperExtensions to create, update, delete, load by id, and list mapped records.
The package is intentionally small. DapperExtensionsDataStore<T> handles CRUD delegation, while DapperExtensionsQueryOptions<T> defines the field predicate metadata that FindAllAsync converts into a Predicates.Field(...) query.
Key APIs
DapperExtensionsDataStore<T> is the package's concrete store type. It derives from DapperDataStore<T, DapperExtensionsQueryOptions<T>>, requires an IDapperDataSource, and is constrained to reference-type DTOs.
CreateAsync(T dto, Action<AsyncOptions> setup = null) inserts a DTO through Source.InsertAsync(dto). The override does not inspect AsyncOptions, so creation behavior comes from the mapped POCO and the underlying Dapper data source.
GetByIdAsync(object id, Action<AsyncOptions> setup = null) loads a single DTO through Source.GetAsync<T>(id). Like the other CRUD overrides, the method keeps the inherited AsyncOptions signature but does not consume the callback.
FindAllAsync(Action<DapperExtensionsQueryOptions<T>> setup = null) is the query customization entry point. When Predicate is left unset it returns Source.GetListAsync<T>(); otherwise it builds a single Predicates.Field(...) expression from Predicate, Op, Value, Not, UseColumnPrefix, Function, and FunctionParameters.
UpdateAsync(T dto, Action<AsyncOptions> setup = null) forwards directly to Source.UpdateAsync(dto). This gives you DapperExtensions-based updates without writing command text yourself.
DeleteAsync(T dto, Action<AsyncOptions> setup = null) forwards directly to Source.DeleteAsync(dto). Consumers work with mapped DTO instances rather than crafting delete statements.
DapperExtensionsQueryOptions<T> carries the field-level lookup settings used by FindAllAsync. Its constructor defaults Op to Operator.Eq, Not to false, UseColumnPrefix to true, Function to DatabaseFunction.None, and leaves Predicate, Value, and FunctionParameters unset until the caller configures them.
Basic usage
using System;
using Codebelt.Extensions.Xunit;
using DapperExtensions.Predicate;
using Savvyio.Extensions.DapperExtensions;
using Xunit;
namespace MyProject.Tests;
public class DapperExtensionsQueryOptionsExamples : Test
{
public DapperExtensionsQueryOptionsExamples(ITestOutputHelper output) : base(output) { }
[Fact]
public void FindAllAsync_ConfiguredPredicate_BuildsFieldPredicate()
{
Action<DapperExtensionsQueryOptions<AccountProjection>> byId = options =>
{
options.Predicate = account => account.Id;
options.Value = 42;
};
var options = new DapperExtensionsQueryOptions<AccountProjection>();
byId(options);
TestOutput.WriteLine($"Lookup configured for {nameof(AccountProjection.Id)} = {options.Value}");
Assert.NotNull(options.Predicate);
Assert.Equal(42, options.Value);
Assert.Equal(Operator.Eq, options.Op);
Assert.False(options.Not);
Assert.True(options.UseColumnPrefix);
Assert.Equal(DatabaseFunction.None, options.Function);
Assert.Null(options.FunctionParameters);
}
private sealed class AccountProjection
{
public int Id { get; set; }
}
}
Use this pattern when you pass a setup delegate to DapperExtensionsDataStore<T>.FindAllAsync and want the store to derive a field predicate instead of requiring handwritten SQL. It matters because the option object captures the predicate selector and comparison settings that FindAllAsync turns into the DapperExtensions query it executes.
Installation
dotnet add package Savvyio.Extensions.DapperExtensions
Usage guidance
Choose this package when your persistence layer already uses IDapperDataSource from Savvyio.Extensions.Dapper and your records map cleanly to DapperExtensions-managed POCOs, because it gives you ready-made CRUD plus a field-based FindAllAsync hook. If you need handwritten SQL, command-text control, or the lower-level DapperQueryOptions model, stay on Savvyio.Extensions.Dapper instead; if you also want DI registration, use the sibling Savvyio.Extensions.DependencyInjection.DapperExtensions package.
Family packages
- 🏭Savvyio.App
- 📦Savvyio.Commands
- 📦Savvyio.Commands.Messaging
- 📦Savvyio.Core
- 📦Savvyio.Domain
- 📦Savvyio.Domain.EventSourcing
- 📦Savvyio.EventDriven
- 📦Savvyio.EventDriven.Messaging
- 📦Savvyio.Extensions.Dapper
- 📦Savvyio.Extensions.DependencyInjection
- 📦Savvyio.Extensions.DependencyInjection.Dapper
- 📦Savvyio.Extensions.DependencyInjection.DapperExtensions
- 📦Savvyio.Extensions.DependencyInjection.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.DependencyInjection.NATS
- 📝Savvyio.Extensions.DependencyInjection.Newtonsoft.Json
- 📦Savvyio.Extensions.DependencyInjection.QueueStorage
- 📦Savvyio.Extensions.DependencyInjection.RabbitMQ
- 📦Savvyio.Extensions.DependencyInjection.SimpleQueueService
- 📝Savvyio.Extensions.DependencyInjection.Text.Json
- 📦Savvyio.Extensions.Dispatchers
- 📦Savvyio.Extensions.EFCore
- 📦Savvyio.Extensions.EFCore.Domain
- 📦Savvyio.Extensions.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.NATS
- 📝Savvyio.Extensions.Newtonsoft.Json
- 📦Savvyio.Extensions.QueueStorage
- 📦Savvyio.Extensions.RabbitMQ
- 📦Savvyio.Extensions.SimpleQueueService
- 📝Savvyio.Extensions.Text.Json
- 📦Savvyio.Messaging
- 📦Savvyio.Queries