Support DynamicParameters by delegating to the bag's own protocol - #195
Merged
Conversation
A DynamicParameters argument is runtime state - no generator can know its members. But the bag already implements the entire vanilla protocol (per-parameter DbType/direction/size/precision/scale, templates, literal replacement, RemoveUnused, storing attached parameters so Get<T> reads outputs), so the generated command factory simply delegates: AddParameters(cmd) via the new identity-free Dapper overload, plus a PostProcess that fires IParameterCallbacks.OnCompleted. That is vanilla's own code path, so behavior is exact - including templates on JIT; under native AOT templates fail inside Dapper's ref-emit exactly as vanilla does there. The generator probes for the AddParameters(IDbCommand) symbol and only takes this path when the referenced Dapper has it - older Dapper keeps the DAP015 refusal, so nothing changes for existing consumers (the full golden suite is untouched against the current package). Dynamic bags already route to the deferred parameter map, which disables command caching; collection/multi-exec detection is skipped for them. On the Dapper test suite (with the Dapper-side overload): 494 -> 533 of 725 call-sites handled; DAP015 drops 114 -> 30 (the remainder are object-typed arguments, which stay refused - announced-types territory).
The Defer decision for DynamicParameters-style bags only applied on the
command-text branch; stored procedures took ParameterMode.All first, so
a proc+bag call-site got an empty map and selected the parameterless
fallback factory - parameters never attached ('expects parameter @id,
which was not supplied'). Caught by the Dapper test suite's proc tests
on the first behavioral run; they go 16 failures to 0 with this (the
two remaining in that file are the known list-expansion gap, unrelated).
Marc's rule for Dapper-side dependencies, now established as the pattern:
probe for the symbol, never emit code that cannot compile against the
referenced Dapper, and refuse with a diagnostic that names exactly which
API is missing and what it enables - not a generic message, and never a
baffling compiler error. The DynamicParameters path previously fell back
to DAP015 ('parameter type could not be resolved'), which described the
symptom rather than the fix; it now reports DAP052 naming
DynamicParameters.AddParameters(IDbCommand). Docs page included; the
verifier runs against the packaged (older) Dapper, which is exactly the
scenario the diagnostic exists for.
Member
Author
|
Added DAP052 per the feature-detection rule: when a Dapper.AOT feature depends on an API in the referenced Dapper (here |
The verifier previously relied on the project-wide Dapper reference not yet exposing AddParameters(IDbCommand) - which means a future package bump silently flips the probe and fails this test. It now references the shipped 2.1.72 package explicitly (a new pinDapperPackageVersion knob on the verifier, which swaps the live assembly for a resolved package), so it guards the probe-and-refuse path permanently, against the genuine old artifact. When the bump happens, the remaining work is the positive twin (same code, live reference, no diagnostic) and a golden fixture for the defer emit - both impossible to write until a released Dapper has the API.
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.
A
DynamicParametersargument is runtime state — no generator can know its members. But the bag already implements the entire vanilla protocol (per-parameter settings, templates, literal replacement,RemoveUnused, storing attached parameters soGet<T>reads outputs), so the generated command factory simply delegates:args.AddParameters(cmd.Command!)via the new identity-free overload (DapperLib/Dapper#2225), plus aPostProcessfiringIParameterCallbacks.OnCompleted. Because it is vanilla's code path, behavior is exact — including templates on JIT; under native AOT templates fail inside Dapper's own ref-emit exactly as vanilla does there.Probe-gated: the generator looks for the
AddParameters(IDbCommand)symbol on the referenced Dapper and only takes this path when present — older Dapper keeps the DAP015 refusal, so the entire golden suite is untouched against the current package, and this can merge independently of the Dapper PR. Dynamic bags route to the deferred parameter map (command caching disabled); one mode fix included: stored procedures previously choseParameterMode.Allbefore the bag Defer check, which handed proc+bag call-sites the parameterless fallback factory.Measured on the Dapper test suite (with the Dapper-side overload via project reference): 494 → 533 of 725 call-sites handled; DAP015 drops 114 → 30 (the remainder are
object-typed arguments, which stay refused); the ProcedureTests DynamicParameters group (output/return params,Get<T>, DateTime2 precision) goes fully green.