DapperLib / DapperLib/Dapper

Async Pipelined execution does not work with Microsoft.Data.SqlClient.

Open
#2,028 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
18.4k
Forks
3.7k
Avg merge
5h 8m
Merged PRs (30d)
1

Description

When migrating from System.Data.SqlClient to Microsoft.Data.SqlClient i stumbled on a strange behavior.
I'm using IDbConnection.ExecuteAsync(new CommandDefinition(...parameters: IEnumerable<T>, flags: CommandFlags.Pipelined)) on a few places in my code.
Works fine with System.Data.SqlClient but fails with Microsoft.Data.SqlClient. The exception i get is:

"System.InvalidOperationException: The method 'EndExecuteNonQuery' cannot be called more the once for the.."

I've looked in to the code, might found the problem, and tried a solution. The current state of the Task management when running async pipline mode with multiple parameters is described in short below. full version here

Foreach param, create a command and =>

var task = cmd.ExecuteNonQueryAsync(command.CancellationToken); 
pending.Enqueue(new AsyncExecState(cmd, task));

When creation of tasks based on multiple params is done =>

while (pending.Count != 0)
{
    var pair = pending.Dequeue();
    using (pair.Command) { /* dispose commands */ }
    total += await pair.Task.ConfigureAwait(false);
}

The exception seems to arise when the pair.Command is disposed and then the pair.Task is awaited.

What seems to fix the problem is to infer the using of the executing command with the corresponding work looking something like e.g:

Command creation based on multiple params =>

async Task<int> ExecuteQueryAsync(DbCommand cmd)
{
   using (cmd)
   {
       return await cmd.ExecuteNonQueryAsync(command.CancellationToken);
   }
}

var task = ExecuteQueryAsync(cmd);
pending.Enqueue(task);

Continuation =>

while (pending.Count != 0)
{
    var task = pending.Dequeue();
    total += await task.ConfigureAwait(false);
}

I'm to novice to get an overview on how this affects the management of the commands, and why it was splitted in the first place. I've tested it locally and the happy path works, but have not tested it extensivly.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Dapper/SqlMapper.Async.cs around line 559, focusing on how pipelined ExecuteAsync tracks commands and tasks. Reproduce the issue with Microsoft.Data.SqlClient and compare command disposal with task completion; done means pipelined execution with multiple parameters completes without the EndExecuteNonQuery exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.