When passing Dictionary<string, object> to Dapper, DataTable values are not properly passed to stored proc TVP arguments
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
I use TVPs in a number of stored procedures, for bulk get, bulk insert, and when searching for multiple records based on a collection of identifiers (GUIDs or strings).
In the case of bulk get and bulk insert, I pass in an anonymous type with one property set as a DataTable and a few other properties related to the sproc execution and logging. This works fine.
In the case of search, I pass in a Dictionary<string, object> containing the stored procedure argument names and values. If a value in the dictionary is of type DataTable (because my stored procedure is expecting a TVP), Dapper returns the following error:
Message:
The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 6 ("@MessageGuids"): Data type 0x62 (sql_variant) has an invalid type for type-specific metadata.
StackTrace:
at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__203_0(Task`1 result)
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location where exception was thrown ---
at Dapper.SqlMapper.QueryAsync[T](IDbConnection cnn, Type effectiveType, CommandDefinition command) in /_/Dapper/SqlMapper.Async.cs:line 418
If instead, I add some logic to convert my Dictionary<string, object> into a DynamicParameters collection, where I check the if a value is a DataTable, and if so I convert the DataTable using the AsTableValuedParameter() method, the stored procedure executes without error and the search returns the expected results. This seems to point out a bug (or shortcoming) in the Dapper processing of parameters as Dictionary<string, object>.
It makes logical sense to me that I should be able to pass in a Dictionary containing a DataTable value and expect Dapper to know what to do with it, just like it already does for anonymous types. Otherwise callers have to manually set up the DynamicParameters collection themselves, which shouldn't be necessary.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Dapper.SqlMapper.Async.cs around line 418 and trace how Dictionary<string, object> values are turned into command parameters. Compare that path with anonymous types and DynamicParameters, focusing on DataTable values and AsTableValuedParameter(). Done means a dictionary containing a DataTable is accepted as a TVP argument and the stored procedure executes successfully without the reported SQL Server error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend, database
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100