Skip to content

Commit 82de73a

Browse files
github-actions[bot]Copilot
authored andcommitted
perf: pre-compute quoted column names for DataTable.Update RowUpdating handler
The RowUpdating event fires once per INSERT row during DataTable.Update(). Previously the handler iterated all columns, called commandBuilder.QuoteIdentifier for every refreshable column, and allocated a ResizeArray on every row. Column metadata (AutoIncrement/AllowDBNull flags) and quoted column names are constant for the lifetime of a single Update() call, so we pre-compute them once before subscribing: - alwaysRefreshQuoted: quoted names for AutoIncrement columns (server always supplies the value) - maybeRefreshCols: (DataColumn, quotedName) pairs for AllowDBNull columns; only added per-row when the row has no explicit value This eliminates O(columns) QuoteIdentifier calls and a full column-iteration per INSERT row, replacing them with a single pre-computation and a cheaper per-row scan over the smaller maybeRefreshCols array. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3a9a563 commit 82de73a

File tree

1 file changed

+19
-8
lines changed

1 file changed

+19
-8
lines changed

src/SqlClient/DataTable.fs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,30 @@ type DataTable<'T when 'T :> DataRow>(selectCommand: SqlCommand, ?connectionStri
5656
selectCommand.Connection <- new SqlConnection( connectionString.Value.Value)
5757

5858
use dataAdapter = new SqlDataAdapter(selectCommand)
59-
use commandBuilder = new SqlCommandBuilder(dataAdapter)
59+
use commandBuilder = new SqlCommandBuilder(dataAdapter)
60+
61+
// Pre-compute per-column metadata once rather than inside the per-row event handler.
62+
// AutoIncrement columns always need a server-side refresh; AllowDBNull columns need one
63+
// only when the row has no explicit value. Quoting column names is also constant.
64+
let alwaysRefreshQuoted =
65+
[| for c in this.Columns do
66+
if c.AutoIncrement then
67+
yield "inserted." + commandBuilder.QuoteIdentifier c.ColumnName |]
68+
let maybeRefreshCols =
69+
[| for c in this.Columns do
70+
if c.AllowDBNull && not c.AutoIncrement then
71+
yield c, "inserted." + commandBuilder.QuoteIdentifier c.ColumnName |]
72+
6073
use __ = dataAdapter.RowUpdating.Subscribe(fun args ->
6174
timeout |> Option.iter (fun x -> args.Command.CommandTimeout <- int x.TotalSeconds)
6275

6376
if args.Errors = null && args.StatementType = StatementType.Insert
6477
&& defaultArg batchSize dataAdapter.UpdateBatchSize = 1
65-
then
66-
let columnsToRefresh = ResizeArray()
67-
for c in this.Columns do
68-
if c.AutoIncrement
69-
|| (c.AllowDBNull && args.Row.IsNull c.Ordinal)
70-
then
71-
columnsToRefresh.Add( "inserted." + commandBuilder.QuoteIdentifier c.ColumnName)
78+
then
79+
let columnsToRefresh = ResizeArray(alwaysRefreshQuoted)
80+
for c, quotedName in maybeRefreshCols do
81+
if args.Row.IsNull c.Ordinal then
82+
columnsToRefresh.Add quotedName
7283

7384
if columnsToRefresh.Count > 0
7485
then

0 commit comments

Comments
 (0)