Skip to content

Add [BindMany] to run a [SqlUpdate] once per item of an IEnumerable parameter - #73

Open
twogood with Copilot wants to merge 8 commits into
mainfrom
copilot/support-batch-sql-updates
Open

twogood with Copilot wants to merge 8 commits into
mainfrom
copilot/support-batch-sql-updates

Conversation

Copilot AI commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

A DAO method builds a single SqlStatement, so there is no equivalent of Dapper's ExecuteAsync(sql, IEnumerable<object>, transaction). Callers have to issue one DAO call per item.

[SqlUpdate("INSERT INTO author(isbn, name) VALUES (@isbn, @AuthorName)")]
Task<int> InsertAuthors(string isbn, [BindMany] IEnumerable<Author> authors, IDbTransaction? tx);
  • [BindMany] attribute
    • Valid only on an IEnumerable<T> parameter of a [SqlUpdate] method.
    • Each item's public properties are bound like [BindProperties]: prop and param_prop, honouring [Bind].
    • All other arguments are shared by every execution, and the transaction argument is forwarded as before.
    • Misuse throws NotSupportedException: non-update method, more than one [BindMany], non-enumerable type, or combined with [BindProperties].
    • A null collection or a null item throws.
    • The sequence is materialized when the method is called, before anything is sent to the gateway.
  • IDatabaseGateway.ExecuteManyAsync(statement, parameterSets)
    • Returns the total affected rows.
    • Breaking for custom gateways: they must implement the new method.
  • DapperGateway
    • Runs one ExecuteAsync per parameter set in statement.Transaction.
    • I did not use Dapper's list overload. It fails with "Must add values for the following parameters" when given a list of DynamicParameters.

Review notes:

  • Dapper matches parameter names case-sensitively, so the SQL must use @Name for a Name property. This is the same as [BindProperties] today.
  • The batch is not wrapped in a transaction by the library. Atomicity depends on the caller passing a transaction.
  • Tests cover commit and rollback with a [BindMany] insert in DapperGatewayTransactionTest. README.md has a new example.

@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9919a14e-65b0-4ebb-92ee-88de7db78127

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-authored-by: twogood <189982+twogood@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for batch SQL updates from IEnumerable parameter sets Add [BindMany] to run a [SqlUpdate] once per item of an IEnumerable parameter Oct 12, 2026
Copilot AI requested a review from twogood October 12, 2026 18:21
@twogood
twogood requested a balanced review from Copilot October 12, 2026 18:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Collection validation and runtime-type reflection violate the declared binding contract, and the README example duplicates a method.

2 open findings
What changed in this PR

Adds declarative batch execution for [SqlUpdate] DAO methods using [BindMany].

Changes:

  • Builds one parameter set per collection item.
  • Extends gateways with ExecuteManyAsync.
  • Adds Dapper transaction tests and documentation.
File Description
README.md Documents batch inserts.
MethodHandler.cs Validates and expands [BindMany] arguments.
IDatabaseGateway.cs Adds the batch execution contract.
BindManyAttribute.cs Defines the new parameter attribute.
DapperGatewayTransactionTest.cs Tests commit and rollback behavior.
DapperGateway.cs Executes and totals repeated updates.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Activout.DatabaseClient/Implementation/MethodHandler.cs Outdated
Comment thread README.md Outdated
@twogood

twogood commented Oct 12, 2026

Copy link
Copy Markdown
Owner

@copilot NOT OK: "DapperGateway
Runs one ExecuteAsync per parameter set in statement.Transaction.
I did not use Dapper's list overload. It fails with "Must add values for the following parameters" when given a list of DynamicParameters."
TODO: Figure out how to use Dapper's ExecuteAsync with list:
var authors = book.Authors.Select(author => new { book.ISBN, author.AuthorName }); await connection.ExecuteAsync(authorSql, authors, transaction);

…sync

Co-authored-by: twogood <189982+twogood@users.noreply.github.com>

Copilot AI commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

@copilot NOT OK: "DapperGateway
Runs one ExecuteAsync per parameter set in statement.Transaction.
I did not use Dapper's list overload. It fails with "Must add values for the following parameters" w...

Updated DapperGateway.ExecuteManyAsync in commit 2f1949b to pass parameterSets.Select(GetDynamicParameters) directly to Dapper's ExecuteAsync list overload.

twogood and others added 2 commits October 12, 2026 22:09
Removed duplicate InsertMany method declaration.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: David Eriksson <david@activout.se>
…perties

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@twogood
twogood requested a balanced review from Copilot October 17, 2026 15:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@twogood
twogood marked this pull request as ready for review October 18, 2026 15:09
@twogood

twogood commented Oct 23, 2026

Copy link
Copy Markdown
Owner

@claude[agent] review

@twogood twogood left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three findings from my review are attached inline.

{
if (!names.Add(parameter.Name))
{
throw new NotSupportedException($"Overlapping binding '{parameter.Name}' ({_method.Name}).");

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must fix: This global duplicate check breaks existing [BindProperties] callers even when the documented parameter_property aliases make the SQL unambiguous. For example, Insert([BindProperties] User first, [BindProperties] User second) with SQL using only @first_id and @second_Name succeeds on the base commit but now throws NotSupportedException: Overlapping binding 'id'. Please remove this check from AddSqlStatementParameters; keep any new collision policy scoped to [BindMany].

}

var task = _gateway.ExecuteManyAsync(statement, sets);
return _isAsync ? task : task.Result;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix: The new batch tests only declare Task<int>, while this returns the raw Task<int> for every async batch update. Please add one [BindMany] DAO method returning plain Task and assert that its rows are inserted. This covers the return form requested by #62 and guards the DispatchProxy assignability behavior.

Comment thread README.md

// Executes the statement once per item, using the item's properties
[SqlUpdate("INSERT INTO user(id, name) VALUES (@id, @Name)")]
Task<int> InsertMany([BindMany] IEnumerable<User> users);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fix: Please state here that batch execution is not atomic unless the caller supplies a transaction. If item two fails after item one succeeds, item one remains committed; the example currently makes that easy to miss.

This branch has not been deployed

No deployments
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.

Support batch SQL updates from IEnumerable parameter sets

4 participants