What's new in 11
Note
Version 11.0.0 is currently a preview (11.0.0-preview01). Please try it and report what you find.
Version 11 is a performance release. Result mapping is rewritten around a cached, typed row plan. See Performance for what that does and does not buy you.
New and improved
- Cached, typed row mapping. The mapping for a result-set shape is compiled once and reused. Before, every query compiled a setter per property, which was a fixed cost of about a millisecond per
Map<T>()in the library's own benchmark. Values are read with typed getters, so nothing is boxed. - Direct read loops.
ToList,ToArray,FirstOrDefault,SingleOrDefault,ToDictionary,ToHashSet,ToImmutableArrayand their async twins read straight from the reader.Skipadvances without mapping andTakestops reading once it is met. They are instance methods onMappedQuery<T>, so the query struct is not boxed; theIMappedQuery<T>extensions remain for generic code. - No per-call closures.
IExecuteReadergained state-passingExecuteReader<TState, TResult>andExecuteReaderAsync<TState, TResult>. Pass astaticlambda and the state it needs. - Leaner commands. A command created from a connection no longer allocates a pool wrapper,
Paramsis created on first use, and async execution skips awaiting when the connection is already open. - Reader behavior.
Map<T>()opens readers withSequentialAccess | SingleResult, andFirstOrDefaultaddsSingleRowwhen nothing is skipped.Map(selector)does not useSequentialAccess, because a selector may read columns in any order.
Fixes
- Channels and Dataflow. With
complete: false, the command no longer runs twice. The mappedToChannel<T>,ToChannelAsync<T>andToTargetBlock<T>overloads complete your target only after every row has been delivered and the reader has been released (before, up to about 1,000 buffered rows could be lost with a bounded target). They no longer lose the last row when the reader fails mid-stream, and they map correctly when the reader has extra unmapped columns. - Connections and commands.
OpenAsync(IDbConnection)no longer opens aDbConnectiontwice.EnsureOpenAsynccloses a connection that finished opening just as cancellation arrived.FirstOrdinalResults<T>(IDbCommand, behavior)honorsbehavior.
Breaking changes
IExecuteReaderhas two new members (above). Custom implementations must add them; theExpressiveCommand*classes already do.- A
NULLin a column mapped to a non-nullable value-type property is the provider's error to raise, wrapped in anInvalidOperationExceptionthat names the property and the column. Map nullable columns toNullable<T>or reference properties. - A field-mapping override that names a property that does not exist throws
ArgumentException(it wasKeyNotFoundException). Only properties with a public setter are mapped. FirstOrdinalResults<T>readsNULLasdefault.ResultsBuffered*andFirstOrdinalResults<T>still buffer into a queue and return a dequeuing enumerable, so items are released to the garbage collector as they are drained.ExpressiveCommandBase.Paramsis created lazily.- Async command execution passes the command's
CancellationTokento the connection open. - Mapped Channel and Dataflow overloads: a mapping failure now faults the target when
completeistrue; the target is never completed or faulted before the reader is released;ArgumentNullException.ParamNamefor a null target is"target"; and a cancelled mapped async channel call succeeds only if the reader had already reported the end of the results, otherwise it throwsOperationCanceledException.
Known limitations in this preview
Retrieve()followed byDequeueAs<T>()is a two-pass route: rows are copied intoobject[]and mapped as they are drained. It is slower and allocates more than a directMap<T>()at larger sizes. For typed results preferMap<T>()orResultsBuffered<T>().- The unmapped
ToChanneloverloads can still lose the row that was in flight when a read fails while the target is full. The cause is in the underlying channel library. - Benchmarks and tests cover the in-memory provider, SQLite and SQL Server LocalDB. Feedback from other providers and workloads is welcome.
Earlier versions are in Earlier release notes.