Skip to content

feat!: Native-AOT compatible store, drop Dapper#29

Merged
idotta merged 4 commits into
mainfrom
aot-modernization
Jul 3, 2026
Merged

feat!: Native-AOT compatible store, drop Dapper#29
idotta merged 4 commits into
mainfrom
aot-modernization

Conversation

@idotta

@idotta idotta commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Makes LiteDocumentStore Native-AOT / trim compatible and removes the Dapper dependency.

  • Serialization routed through System.Text.Json JsonTypeInfo<T> (via DocumentStoreOptions.SerializerOptions); reflection kept only as a quarantined, attribute-suppressed fallback.
  • Dapper removed, replaced by raw ADO.NET helpers in Core/SqliteCommandExtensions.cs (explicit parameter binding, ordinal reads).
  • SchemaIntrospector rewritten without dynamic; MigrationRunner rewritten without Dapper type handlers (applied_at persisted as ISO-8601 round-trip string).
  • Library builds with 0 IL2xxx/IL3xxx warnings; <IsAotCompatible>true</IsAotCompatible>.

Security

Pins SQLitePCLRaw.lib.e_sqlite3 3.50.3 (native binary, no managed deps) to clear NU1903 / CVE-2025-6965Microsoft.Data.Sqlite 10.0.1 otherwise pulls the vulnerable 2.1.11.

Migration robustness

RollbackToVersionAsync now validates every migration definition in the rollback range up front and throws LiteDocumentStoreException if any is missing, instead of silently skipping — a rollback can no longer leave the schema partially reverted.

Breaking changes

  • Removed IDocumentStore.QueryAsync(Expression<Func<T,bool>> predicate) and both SelectAsync projection overloads (required Expression.Compile / reflection). Use QueryAsync(jsonPath, value) or raw SQL via the Connection escape hatch.
  • Removed the public VirtualColumnInfo record.

Housekeeping

  • Tests + benchmarks migrated to the surviving API (benchmarks keep Dapper only as a comparison baseline); tests for removed features deleted.
  • Stale docs de-staled/removed (README.md, .github/instructions/*, old plan docs, stale benchmark reports); added CLAUDE.md.

Verification

  • Library: dotnet build -c Release → 0 warnings, no NU1903.
  • Unit tests: 30/30 pass. Integration tests: 103/103 pass. Benchmarks build.

idotta added 2 commits July 3, 2026 15:53
Route serialization through System.Text.Json JsonTypeInfo<T> (reflection
only as a quarantined fallback), replace Dapper with raw ADO.NET helpers
(SqliteCommandExtensions), and rewrite SchemaIntrospector/MigrationRunner
to avoid dynamic and Dapper type handlers. Library builds with 0 IL2xxx/
IL3xxx warnings.

Pin SQLitePCLRaw.lib.e_sqlite3 3.50.3 to clear NU1903 / CVE-2025-6965
(Microsoft.Data.Sqlite 10.0.1 otherwise pulls the vulnerable 2.1.11).

RollbackToVersionAsync now validates every definition in the range up
front and throws LiteDocumentStoreException instead of silently skipping
a missing one, so a rollback can no longer leave the schema partially
reverted.

Tests and benchmarks updated to the surviving API; docs de-staled.

BREAKING CHANGE: removed IDocumentStore.QueryAsync(Expression predicate)
and both SelectAsync projection overloads (needed Expression.Compile /
reflection, incompatible with AOT). Use QueryAsync(jsonPath, value) or
raw SQL via the Connection escape hatch. Deleted the public
VirtualColumnInfo record.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates LiteDocumentStore to be Native AOT / trimming compatible by removing Dapper and routing all document (de)serialization through System.Text.Json metadata (JsonTypeInfo<T>), while also pruning reflection-heavy query/projection APIs and aligning tests/benchmarks/docs with the surviving surface area.

Changes:

  • Removed Dapper usage from the library and replaced it with internal ADO.NET helpers (SqliteCommandExtensions) using explicit parameter binding + ordinal reads.
  • Made serialization AOT-friendly by requiring caller-provided JsonSerializerOptions (ideally backed by a source-generated JsonSerializerContext), with a quarantined reflection fallback.
  • Removed expression-based predicate querying + projection APIs, updated tests/benchmarks/examples/docs accordingly, and tightened rollback behavior to fail fast on missing migration definitions.

Reviewed changes

Copilot reviewed 46 out of 46 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/tests/LiteDocumentStore.UnitTests/VirtualColumnTests.cs Removed unit tests tied to deleted virtual-column expression translation APIs.
src/tests/LiteDocumentStore.UnitTests/ExpressionToJsonPathTests.cs Removed unit tests for deleted ExpressionToJsonPath.
src/tests/LiteDocumentStore.UnitTests/DocumentStoreTests.cs Removed Dapper usage and updated assertions to new ADO helpers.
src/tests/LiteDocumentStore.IntegrationTests/WalConcurrencyIntegrationTests.cs Removed Dapper dependency in WAL validation assertions.
src/tests/LiteDocumentStore.IntegrationTests/VirtualColumnIntegrationTests.cs Updated virtual column tests to use raw SQL + rehydration (no expression predicates).
src/tests/LiteDocumentStore.IntegrationTests/MigrationIntegrationTests.cs Added coverage for “version below max is skipped” and “missing rollback definition fails fast”.
src/tests/LiteDocumentStore.IntegrationTests/ExceptionIntegrationTests.cs Removed Dapper usage and switched to new parameter binding style.
src/tests/LiteDocumentStore.IntegrationTests/DocumentStoreIntegrationTests.cs Removed predicate/projection tests and migrated Dapper-based checks to ADO helpers.
src/tests/LiteDocumentStore.IntegrationTests/DatabaseSeederExamples.cs Updated seeded-query example to use raw SQL seeks (range predicates no longer supported in API).
src/tests/LiteDocumentStore.Benchmarks/VirtualColumnBenchmark.cs Benchmarks updated to use JSON-path query API instead of expression predicates.
src/tests/LiteDocumentStore.Benchmarks/SimplifiedComparisonBenchmark.cs Updated benchmark calls to the JSON-path query API.
src/tests/LiteDocumentStore.Benchmarks/README.md Removed projection benchmark docs aligned with deleted APIs.
src/tests/LiteDocumentStore.Benchmarks/ProjectionQueryBenchmark.cs Removed projection benchmarks tied to deleted SelectAsync APIs.
src/tests/LiteDocumentStore.Benchmarks/LiteDocumentStore.Benchmarks.csproj Pinned native SQLite bundle for NU1903/CVE mitigation.
src/tests/LiteDocumentStore.Benchmarks/ComparisonBenchmark.cs Updated benchmark calls to the JSON-path query API.
src/LiteDocumentStore/TypeHandlers/DateTimeOffsetHandler.cs Removed Dapper type handler (no longer needed).
src/LiteDocumentStore/Serialization/JsonHelper.cs Switched serialization to JsonTypeInfo<T>-based overloads with reflection fallback quarantined/suppressed.
src/LiteDocumentStore/Migrations/SchemaIntrospector.cs Rewrote schema introspection to avoid dynamic and use ordinal reads.
src/LiteDocumentStore/Migrations/MigrationRunner.cs Removed Dapper usage and persisted applied_at as ISO-8601 round-trip strings; added strict rollback validation.
src/LiteDocumentStore/LiteDocumentStore.csproj Removed Dapper dependency, enabled <IsAotCompatible>true</IsAotCompatible>, pinned native SQLite bundle.
src/LiteDocumentStore/Factories/DocumentStoreFactory.cs Plumbed serializer options into DocumentStore construction.
src/LiteDocumentStore/Factories/DefaultConnectionFactory.cs Removed now-redundant SqliteConnectionExtensions helpers.
src/LiteDocumentStore/Core/VirtualColumnCache.cs Removed cache tied to deleted predicate translation path.
src/LiteDocumentStore/Core/SqliteCommandExtensions.cs Added internal ADO.NET helpers replacing Dapper for common operations.
src/LiteDocumentStore/Core/IDocumentStore.cs Removed expression-predicate query and projection APIs (breaking change).
src/LiteDocumentStore/Core/ExpressionToJsonPath.cs Removed reflection-heavy expression translation code.
src/LiteDocumentStore/Core/DocumentStoreOptionsBuilder.cs Added builder support for configuring serializer options.
src/LiteDocumentStore/Core/DocumentStoreOptions.cs Added SerializerOptions and cloned options support.
src/LiteDocumentStore/Core/DocumentStore.cs Replaced Dapper calls with ADO helpers; routed (de)serialization through caller options; removed predicate/projection methods.
README.md Updated docs for AOT/trim, raw ADO usage, and revised API surface.
PERFORMANCE_INVESTIGATIONS.md Removed stale performance investigation doc.
IMPLEMENTATION_CHECKLIST.md Removed stale implementation checklist doc.
examples/VirtualColumn.cs Updated example to use JSON-path API and raw SQL for index seeks; still includes Dapper as a script dependency.
examples/README.md Updated examples list and added AOT verification example.
examples/ProjectionQuery.cs Removed example tied to deleted projection API.
examples/AotVerification.cs Added AOT smoke-test example using a source-generated STJ context.
CLAUDE.md Added repository guidance (architecture, build/test commands, AOT notes).
benchmarks/2026-01-11/LiteDocumentStore.Benchmarks.VirtualColumnBenchmark-report-github.md Removed stale benchmark report artifact.
benchmarks/2026-01-11/LiteDocumentStore.Benchmarks.SimplifiedComparisonBenchmark-report-github.md Removed stale benchmark report artifact.
benchmarks/2026-01-11/LiteDocumentStore.Benchmarks.ProjectionQueryBenchmark-report-github.md Removed stale benchmark report artifact.
benchmarks/2026-01-11/LiteDocumentStore.Benchmarks.ComparisonBenchmark-report-github.md Removed stale benchmark report artifact.
AOT_COMPATIBILITY_PLAN.md Removed obsolete planning doc (work now implemented).
.github/instructions/testing.instructions.md Removed stale Copilot instructions doc.
.github/instructions/patterns.instructions.md Removed stale Copilot instructions doc.
.github/instructions/general.instructions.md Updated GitHub instructions to reflect new AOT/Dapper-less architecture.
.github/instructions/code-style.instructions.md Updated parameterization guidance to use new ADO helper extensions.

var connection = _connectionFactory.CreateConnection(options);

return new DocumentStore(connection, namingConvention, logger, ownsConnection: true);
return new DocumentStore(connection, namingConvention, logger, ownsConnection: true, options.SerializerOptions);
var connection = await _connectionFactory.CreateConnectionAsync(options).ConfigureAwait(false);

return new DocumentStore(connection, namingConvention, logger, ownsConnection: true);
return new DocumentStore(connection, namingConvention, logger, ownsConnection: true, options.SerializerOptions);
Comment on lines 176 to 179
TableNamingConvention = TableNamingConvention,
AdditionalPragmas = [.. AdditionalPragmas]
AdditionalPragmas = [.. AdditionalPragmas],
SerializerOptions = SerializerOptions
};
Comment on lines 84 to 86
// Use table_xinfo instead of table_info to include generated columns
var sql = $"PRAGMA table_xinfo([{tableName}])";
var pragmaResults = await _connection.QueryAsync(sql).ConfigureAwait(false);

idotta added 2 commits July 3, 2026 16:13
- Microsoft.* framework packages 10.0.1 -> 10.0.9
- Microsoft.NET.Test.Sdk 18.0.1 -> 18.7.0
- coverlet.collector 6.0.4 -> 10.0.1
- Microsoft.SourceLink.GitHub 8.0.0 -> 10.0.300
- Dapper (benchmarks) 2.1.66 -> 2.1.79

SQLitePCLRaw.lib.e_sqlite3 stays pinned at 3.50.3 (still overrides the
vulnerable 2.1.11 that Microsoft.Data.Sqlite 10.0.9 pulls transitively).
xunit kept on the 2.9.3 line; xunit.runner.visualstudio on stable 3.1.5.
GetColumnsAsync interpolated the (public-API) table name into
PRAGMA table_xinfo([...]). SQLite's [ ] quoting has no escape for a
literal ']', so a name containing one broke out of the identifier. Use
double-quote identifier quoting with "" escaping instead.

Also document that DocumentStoreOptions.Clone shares SerializerOptions by
reference intentionally (source-gen resolver + cache; STJ freezes options
after first use).
@idotta

idotta commented Jul 3, 2026

Copy link
Copy Markdown
Owner Author

Reviewed the Copilot comments — dispositions:

  • DocumentStoreFactory.cs:60 & :74 (named arg followed by positional won't compile) — false positive. The ctor is (connection, tableNamingConvention, logger, ownsConnection, serializerOptions); ownsConnection: true sits in its natural 4th position, so a trailing positional 5th arg is legal since C# 7.2. The library compiles clean (0 errors, 0 IL warnings). No change.

  • SchemaIntrospector.cs:86 (identifier injection) — valid, fixed in 35af1e4. PRAGMA table_xinfo([...]) interpolated the public-API table name, and SQLite's [ ] quoting has no escape for a literal ]. Switched to double-quote identifier quoting with "" escaping; added a regression test (GetColumnsAsync_TableNameWithClosingBracket_IsEscaped).

  • DocumentStoreOptions.Clone (SerializerOptions by reference) — intentional, documented in 35af1e4. JsonSerializerOptions carries the source-generated TypeInfoResolver + metadata cache that must be shared for AOT correctness/perf, and STJ freezes the instance after first use (so it's effectively immutable). Deep-cloning would break resolver caching. Kept by-reference with an explaining <remarks>.

All green after the fix: library 0 IL/0 NU1903, unit 30/30, integration 104/104.

@idotta idotta merged commit 8e432cb into main Jul 3, 2026
6 checks passed
@idotta idotta deleted the aot-modernization branch July 3, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants