Skip to content

Commit 810b670

Browse files
vkuttypCopilot
andcommitted
feat: add FillDataTableAsync() for responsive streaming into DataGridView
Batches rows (default 500) with BeginLoadData/EndLoadData per batch and awaits Task.Yield() between batches so the UI message pump stays responsive — form stays clickable while large result sets stream in. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 386ca7f commit 810b670

1 file changed

Lines changed: 70 additions & 21 deletions

File tree

src/SqlDotnetty.Core/ISqlDatabase.cs

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,6 @@ public static class SqlRowExtensions
132132
/// <paramref name="table"/>. Columns are added to the table automatically on the first call
133133
/// if the table has no columns yet.
134134
/// </summary>
135-
/// <example>
136-
/// <code>
137-
/// var dt = new DataTable("Orders");
138-
/// await foreach (var row in conn.QueryStreamAsync("SELECT * FROM Orders"))
139-
/// {
140-
/// row.AddToDataTable(dt);
141-
/// // update UI incrementally here
142-
/// }
143-
/// dataGridView1.DataSource = dt;
144-
/// </code>
145-
/// </example>
146135
public static System.Data.DataRow AddToDataTable(this SqlRow row, System.Data.DataTable table)
147136
{
148137
if (table.Columns.Count == 0)
@@ -161,17 +150,7 @@ public static System.Data.DataRow AddToDataTable(this SqlRow row, System.Data.Da
161150
/// <summary>
162151
/// Converts this <see cref="SqlRow"/> to a standalone <see cref="System.Data.DataRow"/>
163152
/// belonging to a new <see cref="System.Data.DataTable"/> (not yet added to any table).
164-
/// Useful when you need the <c>DataRow</c> without a pre-existing <c>DataTable</c>.
165153
/// </summary>
166-
/// <example>
167-
/// <code>
168-
/// await foreach (var row in conn.QueryStreamAsync("SELECT * FROM Orders"))
169-
/// {
170-
/// var dataRow = row.ToDataRow();
171-
/// // dataRow.Table holds the schema, dataRow[0] etc. hold CLR values
172-
/// }
173-
/// </code>
174-
/// </example>
175154
public static System.Data.DataRow ToDataRow(this SqlRow row)
176155
{
177156
var dt = new System.Data.DataTable();
@@ -184,4 +163,74 @@ public static System.Data.DataRow ToDataRow(this SqlRow row)
184163
dt.Rows.Add(dataRow);
185164
return dataRow;
186165
}
166+
167+
/// <summary>
168+
/// Streams <paramref name="rows"/> into <paramref name="table"/> in batches, suspending
169+
/// DataTable notifications per batch so the UI message pump stays responsive.
170+
/// Bind <paramref name="table"/> to your DataGridView <em>before</em> calling this method —
171+
/// the grid will refresh after each batch rather than after every individual row.
172+
/// </summary>
173+
/// <param name="rows">The async row stream from <c>QueryStreamAsync</c>.</param>
174+
/// <param name="table">A DataTable already bound to a DataGridView (or similar).</param>
175+
/// <param name="batchSize">Rows to accumulate before flushing to the DataTable. Default 500.</param>
176+
/// <param name="ct">Cancellation token.</param>
177+
/// <example>
178+
/// <code>
179+
/// var dt = new DataTable();
180+
/// dataGridView1.DataSource = dt; // bind upfront
181+
///
182+
/// await conn.QueryStreamAsync("SELECT * FROM BigTable")
183+
/// .FillDataTableAsync(dt); // rows appear in batches of 500
184+
/// </code>
185+
/// </example>
186+
public static async Task FillDataTableAsync(
187+
this IAsyncEnumerable<SqlRow> rows,
188+
System.Data.DataTable table,
189+
int batchSize = 500,
190+
CancellationToken ct = default)
191+
{
192+
var batch = new List<SqlRow>(batchSize);
193+
194+
await foreach (var row in rows.WithCancellation(ct).ConfigureAwait(false))
195+
{
196+
// Ensure columns exist from the very first row
197+
if (table.Columns.Count == 0)
198+
{
199+
foreach (var col in row.Columns)
200+
table.Columns.Add(col.Name, typeof(object));
201+
}
202+
203+
batch.Add(row);
204+
205+
if (batch.Count >= batchSize)
206+
{
207+
FlushBatch(table, batch);
208+
batch.Clear();
209+
// Yield to the UI thread so the message pump can process redraws/clicks
210+
await Task.Yield();
211+
}
212+
}
213+
214+
if (batch.Count > 0)
215+
FlushBatch(table, batch);
216+
}
217+
218+
private static void FlushBatch(System.Data.DataTable table, List<SqlRow> batch)
219+
{
220+
table.BeginLoadData(); // suspend DataGridView notifications and index rebuilds
221+
try
222+
{
223+
foreach (var row in batch)
224+
{
225+
var dataRow = table.NewRow();
226+
for (int i = 0; i < row.ColumnCount; i++)
227+
dataRow[i] = row[i].ToClrObject();
228+
table.Rows.Add(dataRow);
229+
}
230+
}
231+
finally
232+
{
233+
table.EndLoadData(); // one bulk notification → one DataGridView refresh
234+
}
235+
}
187236
}

0 commit comments

Comments
 (0)