DapperLib / DapperLib/DapperAOT
1.1.0 regression: a byte[] parameter member is list-expanded instead of bound as one binary parameter
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 472
- Forks
- 43
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 18
Description
Summary
Since 1.1.0 (list expansion, #197), the generator treats a byte[] parameter member as an expandable list and emits SqlMapper.PackListParameters for it. In 1.0.52 the same member was bound as a single parameter, and vanilla Dapper has always bound byte[] as DbType.Binary and never expanded it. Every statement that writes or compares a BLOB through an anonymous or typed parameter object now fails at run time, with no build diagnostic.
Reproduction
net10.0, Dapper 2.1.86, Microsoft.Data.Sqlite 10.0.x, <InterceptorsNamespaces>$(InterceptorsNamespaces);Dapper.AOT</InterceptorsNamespaces>.
using Dapper;
using Microsoft.Data.Sqlite;
[module: DapperAot]
using var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
connection.Execute("CREATE TABLE blobs (id INTEGER PRIMARY KEY, data BLOB NOT NULL)");
byte[] payload = [1, 2, 3, 4];
connection.Execute("INSERT INTO blobs (id, data) VALUES (@Id, @Data)", new { Id = 1, Data = payload });
Console.WriteLine(connection.QuerySingle<long>("SELECT length(data) FROM blobs WHERE id = 1"));
| Dapper.AOT | Result |
|---|---|
| 1.0.52 | prints 4 |
| 1.1.0 | SqliteException: SQLite Error 1: 'row value misused' |
The SQL is rewritten to VALUES (@Id, (@Data1,@Data2,@Data3,@Data4)). Other shapes we hit in real code: an empty array in a comparison (WHERE name_bytes > @After) silently returns no rows, and a large blob fails with too many SQL variables.
Generated code
1.0.52:
p = cmd.CreateParameter();
p.ParameterName = "Data";
p.Direction = global::System.Data.ParameterDirection.Input;
p.Value = AsValue(typed.Data);
ps.Add(p);
1.1.0:
#pragma warning disable CS0618 // list-expansion: this *is* the library usage
global::Dapper.SqlMapper.PackListParameters(cmd.Command!, "Data", typed.Data);
#pragma warning restore CS0618
UpdateParameters also drops the member in 1.1.0.
Expected
byte[] (and, to match vanilla's type map, anything else vanilla maps to a scalar DbType before it considers IEnumerable) is bound as a single parameter, as in 1.0.52 and in vanilla Dapper.
Workaround
Give the member the static type object, which takes the scalar path; the provider then binds the runtime byte[] as one BLOB:
new { Id = 1, Data = (object)payload }
Impact
Found when upgrading four applications from 1.0.52: all BLOB writes failed (pictures, sealed payloads, attachment chunks) and one keyset-pagination query returned nothing. Existing tests caught it; nothing at build time did. A DAP diagnostic would not be the right fix, since this is ordinary, valid Dapper usage.
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 with the generator path that emits SqlMapper.PackListParameters and the UpdateParameters handling for byte[] members, comparing it with the shown 1.0.52 scalar-parameter output. Reproduce the INSERT against Microsoft.Data.Sqlite, then verify byte[] values are bound as one binary parameter for inserts and comparisons, including empty and large arrays.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100