@@ -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