You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Undocumented breaking API changes between 10.0.2 and 10.2.0 (TempTableQuery<T> ctor, IBulkInsertExecutor.BulkInsertAsync) #74
While upgrading Thinktecture.EntityFrameworkCore.SqlServer from 10.0.2 to 10.2.0, I hit two source-breaking public API changes that don't appear to be called out in the release notes / changelog. Both look intentional and reasonable — I'm filing this so the breaking nature is documented for others doing the upgrade.
Tracing it back, both changes were introduced together in commit feat: add bulk insert/update from query and first shipped in 10.1.0 (not 10.2.0). That feature needs the affected-row count, which is presumably why the count now flows through these two APIs — so this is really about flagging the side effects of that feature as breaking.
1. IBulkInsertExecutor.BulkInsertAsync<T> return type changed (Task → Task<int>)
Any custom IBulkInsertExecutor implementation (we have a test fake) no longer satisfies the interface:
'FakeInMemoryBulkInsertExecutor' does not implement interface member 'IBulkInsertExecutor.BulkInsertAsync(...)' ... because it does not have the matching return type of 'Task'
2. TempTableQuery<T> constructor gained a required parameter
The 2-arg constructor was replaced by a 3-arg one (it's a replacement, not an added overload), backed by a new ITempTableQuery<T>.NumberOfInsertedRows property:
- public TempTableQuery(IQueryable<T> query, ITempTableReference tempTableReference)+ public TempTableQuery(IQueryable<T> query, ITempTableReference tempTableReference, int numberOfInsertedRows)
Code that constructs TempTableQuery<T> directly (e.g. a custom temp-table population helper that fills the table itself and then wraps it) fails to compile:
There is no argument given that corresponds to the required parameter 'numberOfInsertedRows' of 'TempTableQuery.TempTableQuery(IQueryable, ITempTableReference, int)'
Environment
Thinktecture.EntityFrameworkCore.SqlServer 10.0.2 → 10.2.0 (changes originate in 10.1.0)
.NET 10 / EF Core 10
Request
Could the release/migration notes for 10.1.0 call these out as source-breaking changes? Specifically:
IBulkInsertExecutor.BulkInsertAsync now returns Task<int> — breaks custom implementations.
The TempTableQuery<T> 2-arg constructor was replaced by a 3-arg one requiring numberOfInsertedRows — breaks direct constructor calls.
And, if it's the intended public path, a short pointer on how a caller that populates a temp table itself should obtain the numberOfInsertedRows value to pass to the constructor (e.g. from the affected-row count of the insert command).
Thanks for the detailed write-up. These are intentional changes, and you correctly traced them to the "bulk insert/update from query" feature first shipped in 10.1.0 (not 10.2.0). That feature needs the affected-row count, so the count now flows through these two APIs.
What's changing and why
IBulkInsertExecutor.BulkInsertAsync<T> now returns Task<int> — the number of inserted rows (consistent with the other bulk operations, which already return Task<int>). Custom implementations need to return the count.
TempTableQuery<T> 2-arg ctor → 3-arg ctor (int numberOfInsertedRows), backed by the new ITempTableQuery<T>.NumberOfInsertedRows. This lets callers read how many rows landed in the temp table without a separate COUNT(*).
How to obtain numberOfInsertedRows when you populate the temp table yourself
Use the affected-row count of your own insert. That's exactly what the library does internally, it passes through the value returned by the bulk insert:
// number of rows your insert reported as affectedvarnumberOfInsertedRows=awaitdbContext.BulkInsertAsync(entities,options,ct);returnnewTempTableQuery<T>(query,tempTableReference,numberOfInsertedRows);
If you populate the table with your own command instead, use whatever row count that path reports.
Activity
PawelGerr commentedon Jun 24, 2026
Thanks for the detailed write-up. These are intentional changes, and you correctly traced them to the "bulk insert/update from query" feature first shipped in 10.1.0 (not 10.2.0). That feature needs the affected-row count, so the count now flows through these two APIs.
What's changing and why
IBulkInsertExecutor.BulkInsertAsync<T>now returnsTask<int>— the number of inserted rows (consistent with the other bulk operations, which already returnTask<int>). Custom implementations need to return the count.TempTableQuery<T>2-arg ctor → 3-arg ctor (int numberOfInsertedRows), backed by the newITempTableQuery<T>.NumberOfInsertedRows. This lets callers read how many rows landed in the temp table without a separateCOUNT(*).How to obtain
numberOfInsertedRowswhen you populate the temp table yourselfUse the affected-row count of your own insert. That's exactly what the library does internally, it passes through the value returned by the bulk insert:
If you populate the table with your own command instead, use whatever row count that path reports.