Skip to content

ExecuteScalar<T> converts the way Map<T>() does: a bigint read as int, a numeric as long - #123

Merged
electricessence merged 4 commits into
masterfrom
fix/execute-scalar-converts
Nov 12, 2026
Merged

electricessence merged 4 commits into
masterfrom
fix/execute-scalar-converts

Conversation

@electricessence

Copy link
Copy Markdown
Collaborator

ExecuteScalar<T> and ExecuteScalarAsync<T> convert the way Map<T>() does, so a port that keeps the same T no longer fails only on a real database.

The problem

  • ExecuteScalar<T> cast the value exactly and threw InvalidCastException when the column type differed from T: a bigint serial read as int, or a numeric identity read as long. A single-value Map<T>() converts, and so does Dapper.

What changes

  • ExecuteScalar<T>(), ExecuteScalarAsync<T>(), ExecuteReturn<T>() and ExecuteReturnAsync<T>() use the single-value conversion Map<T>() uses:
    • a value that is already a T is returned as is;
    • NULL, DBNull and no row give default(T), as before;
    • anything else is converted with the same range check: a bigint into int, a numeric into long, an int into long. A value out of range throws ValueMappingException with the OverflowException inside, the same as Map<int>().
  • The conversion happens after the command has finished, so the connection is released first, as before. Nothing is cast any more, so no exact-cast variant is kept.
  • The Func<object?, T> transform overloads are unchanged. No public signature changes, so PublicAPI.Unshipped.txt is untouched.

Behavior change

  • Calls that threw InvalidCastException now convert, or throw ValueMappingException on overflow. Noted in whats-new-11.md.

Tests

  • A new matrix of about 100 value and target pairs compares each scalar with Map<T>() over a typed reader (the same value, or the same failure type and message), sync and async.
  • Named cases: bigint into int, numeric into long, int into long, overflow, NULL and no row, a failed conversion leaving the connection as it was found, cancellation.
  • A compiled sample reads a bigint as int; the old "failed cast" check in the samples is replaced by three checks.

Checks

  • Release and Debug builds: 0 warnings, 0 errors. Tests: Debug 9696 of 9696. Release 9696 of 9696 on the third run; the first two Release runs each failed one of two known flaky tests that this change does not touch (TokenTerminalTests.Allocation_Channel, then a LeakPipelineTests Channel case), which are being fixed separately. Samples: 105 of 105 checks pass. Style and analyzers: 0 findings. DocFX: 0 warnings.

🤖 Generated with Claude Code

electricessence and others added 4 commits November 12, 2026 04:19
…, a numeric as long); overflow throws

The exact cast threw InvalidCastException when the column type differed from T. ExecuteScalar<T>,
ExecuteScalarAsync<T>, ExecuteReturn<T> and ExecuteReturnAsync<T> now use the single-value conversion
Map<T>() uses: the value as is when it is already a T, NULL and no row to default, else a range-checked
conversion that throws ValueMappingException on overflow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…<T>() uses, not "the general conversion" or "cannot drift apart"

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@electricessence
electricessence merged commit 5d8ea66 into master Nov 12, 2026
2 checks passed
@electricessence
electricessence deleted the fix/execute-scalar-converts branch November 12, 2026 15:13
electricessence added a commit that referenced this pull request Nov 12, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
electricessence added a commit that referenced this pull request Nov 12, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
electricessence added a commit that referenced this pull request Nov 12, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.

1 participant