--- title: "v0.5.4 Technical Details" description: "Detailed notes for RoomSharp 0.5.4 core runtime and QueryExtensions implementation updates." canonical: "https://roomsharp.dev/docs/v0.5.4/release-notes-0-5-4-details" source: "src/content/v0.5.4/release-notes-0-5-4-details.mdx" --- # v0.5.4 Technical Details This page records lower-level core and QueryExtensions implementation notes for production review, maintenance, and debugging. ## Core Runtime Details ### Transaction Command Cache Identity Generated serialized DAO code can reuse prepared command templates. When a transaction runs on a different connection than the command template, RoomSharp clones the command for the transaction connection. In `v0.5.4`, the per-transaction clone cache is keyed by the template `DbCommand` reference using a reference comparer. Impact: - Keeps command reuse tied to the exact prepared command template. - Keeps the command clone cache per transaction. - Preserves the generated DAO performance strategy without adding shared mutable command state. ### Seeder Transaction Atomicity Transactional seeding now runs through `RoomDatabase.RunInTransactionAsync`. This aligns all transactional seeder work with the ambient RoomSharp transaction/session model. The intended invariant is: - Seeder raw commands. - DAO calls made by the seeder. - Seeder journal writes. All participate in the same transaction/session when `RunInTransaction` is enabled for the seeder. ### Async Serialized Transactions Serialized `RunInTransactionAsync` now avoids the older sync gate path for async work. The caller's `CancellationToken` is honored while waiting for the transaction lock and while opening the transaction. This is not a public API change, but it improves behavior under load because cancelled work does not have to wait behind a busy transaction queue. ### Global Filter Disable Scopes `GlobalFilterRegistry.Disable(...)` is backed by async-flow-local scopes. `v0.5.4` makes scope disposal robust when disposables are released out of order. Impact: - Disposing an older disable scope no longer clears a newer scope in the same async flow. - Nested disable scopes restore predictably. - Hot-path filter reads continue using snapshot-based lookup. ### Pager Enumeration State `Pager` no longer stores the current page as mutable instance state. Each call to `GetPagesAsync(...)` starts from page `0` and tracks page state locally. Impact: - Re-enumerating the same pager starts from the first page again. - Multiple independent enumerations do not share page cursor state. - Page size comes from `PagingSource.DefaultPageSize` instead of a hidden fixed value. ### FTS Provider SQL MySQL and SQL Server FTS search predicate generation now writes provider-appropriate column-aware expressions: ```sql MATCH(`Title`, `Body`) AGAINST (@q IN BOOLEAN MODE) CONTAINS(([Title], [Body]), @q) ``` Identifiers continue to go through FTS SQL quoting helpers rather than being emitted as plain user-provided strings. ## QueryExtensions Details ### DbSession Transaction Ownership `DbSession.InTransaction*` now checks whether a session already has a transaction before starting one. If the extension starts the transaction, it owns commit/rollback/clear. If the caller already has an active transaction, the extension uses it without committing or rolling it back. This gives session-bound transaction helpers the same clear ownership model as the database-level transaction helpers. ### Predicate Grouping `Where(Expression>)` now keeps compound boolean expressions grouped. Example: ```csharp db.From() .Where(o => (o.Status == "new" || o.Status == "held") && o.TenantId == tenantId) ``` The generated predicate preserves the intended grouping and SQL precedence. ### Identifier Validation and Quoting Fluent string-based APIs now validate and quote identifiers in SQL-building paths where RoomSharp can safely identify table and column names. Covered paths include: - `Select(...)`. - `Where(...)` string column overloads. - `Join(...)` table and column arguments. - `OrderBy(...)` / `OrderByDescending(...)`. - `GroupBy(...)`. - Aggregate column arguments. Use typed expressions for normal application code. Use raw SQL APIs when the input is an intentional SQL fragment rather than an identifier. Values were already parameterized. This change is about table/column/operator strings. Never pass end-user input as a SQL identifier. ### Filtered Includes and RelationLoadFilter Filtered includes now create a `RelationLoadFilter` that can be passed into the core relation loader. This allows relation-load SQL to include the filter condition where possible. RoomSharp still keeps an in-memory predicate for assignment-time behavior, while simple include filters can also participate in relation-load SQL. ### Raw Multi-Mapping Provider Checks Async raw multi-mapping now checks whether the provider command supports `DbCommand` / `DbDataReader` before executing async reader logic. Unsupported providers fail with a clear `NotSupportedException`. This matches the defensive style used by the rest of QueryExtensions. ## Tests Added Or Expanded The 0.5.4 production updates are covered by focused tests for: - Transaction command clone cache identity. - Seeder transaction/session atomicity. - `RunInTransactionAsync` cancellation while waiting for the transaction lock. - Out-of-order global filter scope disposal. - Pager re-enumeration behavior. - Provider-aware FTS SQL. - `DbSession.InTransaction*` commit/rollback ownership. - QueryExtensions predicate grouping and identifier validation. - Async raw multi-mapping command validation. This page intentionally documents implementation details because these fixes affect correctness and production confidence even when public APIs did not change.