Whats New in v4.1 - lobodava/artisan-orm GitHub Wiki
Preparing for Artisan.Orm 4.1.0
This page describes the source being prepared for 4.1.0. It is not an announcement that a 4.1.0 package has been published. Check the package or source revision used by your application; the project version is now 4.1.0, but that alone does not establish publication. The changelog records these changes, while What's New in v4 retains the earlier v4 overview.
The library continues to target netstandard2.1, net8.0, and net10.0. The runtime and analyzer test projects target .NET 8 and .NET 10.
Corrected runtime contracts
| Area | 4.1.0 behavior | Guidance |
|---|---|---|
| Scalar enumeration | Synchronous enumeration includes the first row, including a one-row result. | Remove any workaround that duplicated or skipped a row to compensate. |
| Explicit read factories | A supplied factory handles each row, including scalar/nullable projections and SQL NULL in column zero. | Put null handling and alternate-column selection in that factory. |
| External transactions | A repository uses the supplied transaction's connection and borrows both resources. Disposal detaches them; ownership remains with the caller. | Reject completed transactions and finish/detach an existing transaction before starting a helper-owned one. |
| Automatic mapping caches | Keys distinguish actual runtime types and ordered schemas; automatic table creation also distinguishes table names and selected columns. | A matching type name or SQL command string alone is insufficient. |
| Procedure metadata caches | Metadata is scoped by database/connection identity and procedure, cloned per command, and does not share parameter values. | Do not seed the public unscoped cache expecting AddParams to consume it. |
| String parameters | trimToNull changes the supplied value before truncation or size validation; truncate changes sized parameter values. |
Use the flags directly; an additional normalization workaround is unnecessary. |
| DataReply success | The status helper accepts explicit Ok as well as a null/blank status, then advances to the payload. |
Call it only for procedures using that protocol; do not advance a second time on success. |
Additional 4.1.0 corrections cover:
- Decimal precision/scale validation and actual truncation toward zero when requested; dictionary parameter keys accept an optional
@. - Numeric enums in automatic mapping, including nullable enums; indexers and inaccessible getters/setters are skipped where appropriate. Missing runtime mapper write functions produce descriptive mapping errors.
- Cancellation through async connection opening, execution, row reads, and result advancement. Synchronous user callbacks still control their own cancellation checks.
- Original exception preservation during transaction cleanup, and command lifetime through deferred repository enumeration.
- Finite negative-ID exhaustion without wraparound and disposal of acquired MergeJoin enumerators.
- Rejection of undefined/composite DataReply statuses, informative status-only exception messages, and safer cancellation/trace handling in the test-only service sample.
For procedure metadata, SQL-login reuse remains scoped to the server, database, principal, procedure, and relevant settings. Integrated/token authentication reuses metadata within a connection instance. Unqualified procedure names or an unknown default database are not cached. Explicit Add*Param calls avoid metadata discovery. See command initialization, automatic mapping, read methods, transactions, and DataReply.
Mapper self-checks
The bundled Roslyn analyzer checks mapper registrations, required functions, signatures, and supported mapper-dependent calls during builds and editor analysis. Its compiler host requires Roslyn 4.8 or later. RequiredMethod.AllMain consistently requires CreateObject, CreateDataTable, and CreateDataRow; All additionally requires CreateObjectRow.
Choose the requirement that describes the mapper's intended responsibilities, including operations with no callers yet. A read-only mapper can declare CreateObject. A manually written mapper can deliberately use a SQL shape different from the C# model. The analyzer does not connect to SQL Server or infer a live procedure/UDTT schema. See RequiredMethod and compile-time validation.
Sample SQL hierarchy corrections
These changes belong to the repository's sample database, not an automatic migration performed by installing the NuGet package:
dbo.RecalcSubFolderHidsderives the old parent's path from@OldParentHid, not its integer ID.- The initial
ROW_NUMBERpartitions byParentId, so both affected sibling groups are numbered independently after a move. dbo.GetHidCodereturns up to 1784 hexadecimal characters, covering the full 892-bytehierarchyidrepresentation without truncation.
The Users sample also corrects dbo.SaveUsers: name/email duplicate checks use their own rankings, and new-only negative-ID batches are validated. Apply that procedure explicitly when testing or adopting this sample.
Apply the relevant SQL object changes to a test database before running their integration regressions. Building the SQL project does not deploy them. A full sample publish runs GenerateFolders, which resets folder data; review deployment scripts before choosing that route. See hierarchy guidance and FolderSqlRegressionTest.
Graph ordering and deadlocks
The graph-saving algorithm correlates keys using OUTPUT inserted.Id, source.Id. Its parent-child relationships do not depend on insertion order or the order of rows returned by OUTPUT. To preserve a user's collection order, carry an explicit ordinal through the mapper/TVP, persist it, and read with ORDER BY.
Clustered indexes support efficient matching and access; they do not guarantee deadlock freedom or a particular MERGE insertion sequence. INSERT ... SELECT ... ORDER BY has a narrower documented guarantee for identity-value computation, not physical insertion order. See graph saving and ordering for the distinction and transactions for deadlock handling.
The original index assertion remains in Article 1 as historical text, followed by the correction and practical instructions. The current sample's message-based RAISERROR handlers can replace an original error number with 50000; preserving 1205 with THROW; is relevant when designing a deadlock retry policy. The library does not add automatic retries.
Documentation, skill, and dependencies
The four design articles retain their publication history and label differences between historical examples and current source. DataReply remains optional; the current article specifies JSON field serialization, status strings, null omission, and an illustrative HTTP policy explicitly.
The repository's Claude Code skill includes these contracts and focused behavioral scenarios. Updating the skill files does not prove automatic selection in Claude Code; that requires a separate recorded run.
Dependencies include Microsoft.Data.SqlClient 7.1.1, patched transitive PKCS versions, test Configuration packages 10.0.14, Microsoft.NET.Test.Sdk 18.11.0, and MSTest 4.5.2. SourceLink comes from the pinned .NET SDK instead of an explicit SourceLink package. Roslyn and coverlet were not upgraded in this package-update batch. Consult the project files and global.json for exact target-specific requirements.
Verification and migration
Build the whole solution with Visual Studio MSBuild and the SSDT targets, then run both test projects for .NET 8 and .NET 10. Run targets sequentially when they share the seeded test database. Regression coverage includes first-row/scalar projections, borrowed transactions, mapping/parameter cache isolation, string flags, DataReply success, and folder SQL corrections.
For an application migration, check the affected behavior above against its actual repositories and SQL contracts. Database-backed checks use an explicitly configured test connection; they are not an implicit schema-validation feature of the library. See testing your repository.