From 60c346cda771868bd737987ff8d7c24e2fb9b508 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 13:32:51 +0000 Subject: [PATCH 1/2] fix: write back output parameters in AsyncExecuteNonQuery and AsyncExecuteSeq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sync ExecuteNonQuery and ExecuteSeq methods correctly write output parameter values back to the parameters array after execution, so callers can read the updated values. The async counterparts (AsyncExecuteNonQuery and AsyncExecuteSeq) did not do this, leaving output parameter values unreflected in the callers' parameter array. then apply the same output-parameter write-back loop used by the sync path. AsyncExecuteSeq: mirror the sync ExecuteSeq logic. When no output parameters are present, stream as before. When output parameters exist, materialize the result list first (ensuring the reader is consumed before inspecting output param values), write back the output params, then return the materialized result with the appropriate rank (SingleRow → option, ScalarValue → exactlyOne, Sequence → list). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/SqlClient/ISqlCommand.fs | 70 +++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/src/SqlClient/ISqlCommand.fs b/src/SqlClient/ISqlCommand.fs index 2cdfeb96..5215f8d0 100644 --- a/src/SqlClient/ISqlCommand.fs +++ b/src/SqlClient/ISqlCommand.fs @@ -353,30 +353,52 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio box resultset - static member internal AsyncExecuteSeq<'TItem> (rank, rowMapper) = fun(cmd, getReaderBehavior, parameters, expectedDataReaderColumns) -> - let xs = - async { - let! reader = ``ISqlCommand Implementation``.AsyncExecuteReader(cmd, getReaderBehavior, parameters, expectedDataReaderColumns) - return reader.MapRowValues<'TItem>( rowMapper) - } + static member internal AsyncExecuteSeq<'TItem> (rank, rowMapper) = fun(cmd: SqlCommand, getReaderBehavior, parameters, expectedDataReaderColumns) -> + let hasOutputParameters = cmd.Parameters |> Seq.cast |> Seq.exists (fun x -> x.Direction.HasFlag(ParameterDirection.Output)) - if rank = ResultRank.SingleRow - then + if not hasOutputParameters then + let xs = + async { + let! reader = ``ISqlCommand Implementation``.AsyncExecuteReader(cmd, getReaderBehavior, parameters, expectedDataReaderColumns) + return reader.MapRowValues<'TItem>( rowMapper) + } + + if rank = ResultRank.SingleRow + then + async { + let! xs = xs + return xs |> Seq.toOption + } + |> box + elif rank = ResultRank.ScalarValue + then + async { + let! xs = xs + return xs |> Seq.exactlyOne + } + |> box + else + assert (rank = ResultRank.Sequence) + box xs + else async { - let! xs = xs - return xs |> Seq.toOption + let! reader = ``ISqlCommand Implementation``.AsyncExecuteReader(cmd, getReaderBehavior, parameters, expectedDataReaderColumns) + let resultset = reader.MapRowValues<'TItem>( rowMapper) |> Seq.toList + for i = 0 to parameters.Length - 1 do + let name, _ = parameters.[i] + let p = cmd.Parameters.[name] + if p.Direction.HasFlag(ParameterDirection.Output) then + parameters.[i] <- name, p.Value + return + if rank = ResultRank.SingleRow then + resultset |> Seq.toOption |> box + elif rank = ResultRank.ScalarValue then + resultset |> Seq.exactlyOne |> box + else + assert (rank = ResultRank.Sequence) + box resultset } |> box - elif rank = ResultRank.ScalarValue - then - async { - let! xs = xs - return xs |> Seq.exactlyOne - } - |> box - else - assert (rank = ResultRank.Sequence) - box xs static member internal ExecuteNonQuery manageConnection (cmd, _, parameters, _) = ``ISqlCommand Implementation``.SetParameters(cmd, parameters) @@ -394,7 +416,13 @@ type ``ISqlCommand Implementation``(cfg: DesignTimeConfig, connection: Connectio ``ISqlCommand Implementation``.SetParameters(cmd, parameters) async { use _ = cmd.Connection.UseLocally(manageConnection ) - return! cmd.AsyncExecuteNonQuery() + let! recordsAffected = cmd.AsyncExecuteNonQuery() + for i = 0 to parameters.Length - 1 do + let name, _ = parameters.[i] + let p = cmd.Parameters.[name] + if p.Direction.HasFlag(ParameterDirection.Output) then + parameters.[i] <- name, p.Value + return recordsAffected } #if WITH_LEGACY_NAMESPACE From c3e5395e53805f21f33a9ef75bca75d469cfc303 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 13:39:15 +0000 Subject: [PATCH 2/2] ci: trigger checks