Repository navigation
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2234.
The bug
With Dapper 2.1.86 and Microsoft.Data.SqlClient, mapping a
sql_variantcolumn onto a value-type member throws:The same code works in 2.1.79, and casting to
intorbitin the SQL works in 2.1.86.Root cause
SERVERPROPERTYreturnssql_variant, and SqlClient reports such a column's field type asSystem.Object(GetFieldType(0)returnsSystem.Objectwhile the actual value is a boxedInt32— verified against a live SQL Server). The deserializer therefore has to decide at runtime, from the boxed value, how to convert it to the member type.The change in #2228 added
colType == typeof(object)to the direct-unbox shortcut inLoadReaderValueOrBranchToDBNullLabel(SqlMapper.cs), so for a column reported asobjectthe emitted IL now does a blindunbox.anyof the target member type. Unboxing a boxedInt32asboolis an invalid cast, hence the exception. Before that change, this combination fell through toFlexibleConvertBoxedFromHeadOfStack, which converts the boxed value to the member type.The fix
For a column reported as
objectwhose target member is a value type, the deserializer now emits a runtime check of the boxed value's type:DateTimevalue landing in aDateTimemember.Int32→bool,Int64→int, etc. convert the way they used to.Reference-type members keep the previous direct behaviour, type handlers still take priority, and columns with a declared field type are completely unaffected. As a side effect this also fixes the nullable case (
bool?over a variant column), which threw even in 2.1.79 because the conversion result was never wrapped into theNullable<T>.Tests
New
SqlVariantTests(database-free; a stub reader that reports the column asSystem.Object, exactly like SqlClient does forsql_variant):VariantInt32MapsToBool— the reported scenario (0 → false, 1 → true)VariantInt32MapsToNullableBool— includingDBNull→ nullVariantInt32MapsToBoolStruct— struct rows go through the same code pathVariantExactBoxMapsDirectly— exact-type boxes (int, DateTime) still unbox directlyVariantConvertibleBoxConverts— Int64 → int, Double → decimalVariantBoxTypeCanVaryPerRow— one query whose rows carry different underlying typesDeclaredInt32StillMapsToBool— declared-type columns unchangedAlso verified end-to-end against a local SQL Server 2022 with Microsoft.Data.SqlClient 7.0.1:
SERVERPROPERTY('IsLocalDB')→boolfails on currentmainwith the exact error from the issue and maps correctly with this change;sql_variantint →bool/bool?/intand string variants all behave. The Microsoft.Data.SqlClient test classes (Misc, MultiMap, QueryMultiple, Async, TypeHandler, Enum, Decimal, Null, DataReader, DateTimeOnly, Constructor, Parameter — 307 tests) pass, except 3 ParameterTests culture/collation cases that fail identically on unmodifiedmainin the same environment.