Skip to content

DynamicParameters.AddParameters(IDbCommand): identity-free self-apply - #2225

Merged
mgravell merged 2 commits into
mainfrom
dynamicparameters-apply
Aug 20, 2026
Merged

mgravell merged 2 commits into
mainfrom
dynamicparameters-apply

Conversation

@mgravell

Copy link
Copy Markdown
Member

The full bag protocol — per-parameter DbType/direction/size/precision/scale, templates, literal replacement, RemoveUnused — already lives in AddParameters(command, identity), but the identity requirement makes it uncallable from outside: Identity's constructor is internal, and identity.Sql is consumed on the first line. This overload builds the identity from the command's own text and type (which is what identity.Sql is in practice), letting external tooling apply a parameter bag without reimplementing the protocol.

The immediate consumer is Dapper.AOT: its generated command factories can now support DynamicParameters arguments by delegating to the bag itself — which gives exact vanilla behavior for the whole surface (including Get<T> output reads and IParameterCallbacks) because it is the vanilla implementation. The generator probes for this symbol, so it activates only when the referenced Dapper has it; no coordination needed on release timing.

Test covers the bag + literal-replacement composition against a closed connection (no database needed). API registered in PublicAPI.Unshipped.txt.

The full bag protocol (per-parameter DbType/direction/size, templates,
literal replacement, RemoveUnused) already lives in AddParameters(command,
identity), but the identity requirement makes it uncallable from outside:
Identity's constructor is internal, and identity.Sql is consumed on the
first line. This overload builds the identity from the command's own text
and type, which is what identity.Sql is in practice - letting external
tooling (for example build-time code generators, aka Dapper.AOT) apply a
bag without reimplementing the protocol.

Test covers the bag+literal composition against a closed connection (no
database needed).
A subclass that hides AddParameters and re-implements the interface - the
DynamicParameterWithIntTVP pattern from this very test suite - was skipped
by the direct call, which binds statically to the protected base method.
Routing through the interface uses the runtime type's interface map, so
the subclass version runs, matching what vanilla execution does.
@mgravell

Copy link
Copy Markdown
Member Author

Pushed a follow-up: the overload now dispatches via (SqlMapper.IDynamicParameters)this rather than calling the protected method directly. A subclass that hides AddParameters and re-implements the interface — the DynamicParameterWithIntTVP pattern from this repo's own test suite — was otherwise skipped, because the direct call binds statically to the base method. Test added for the hiding-subclass shape.

@mgravell
mgravell merged commit 41d76c7 into main Aug 20, 2026
2 checks passed
This was referenced Sep 12, 2026
This was referenced Sep 21, 2026
mgravell added a commit that referenced this pull request Sep 23, 2026
…2238)

main.yml filtered pushes with

  paths:
  - '*'
  - '!/docs/*'

GitHub's `*` does not cross a '/', so `'*'` matches only files at the repository
root. Any push touching just Dapper/, tests/ or another subdirectory failed the
filter and the build was skipped - silently, because a skipped workflow is not a
failure. Checking main:

  8becae8 (#2230)  4 root files  ran
  2ca8261 (#2223)  1 root file   ran
  6d48ef6 (#2228)  0 root files  skipped
  41d76c7 (#2225)  0 root files  skipped

So most merges to main never built at all. The second line never worked either:
path filters are repo-relative and take no leading slash, so '!/docs/*' matched
nothing - the docs exclusion it was written for was already being done, by
accident, by the '*' that excluded every subdirectory.

paths-ignore: 'docs/**' says what was meant: run for everything except a
docs-only change.

This matters more than it used to. release.yml takes its version from what a
green main run reports, so a main build that never runs means no version to tag
with - which is how this was noticed.
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