Reputation: 1296
I've created an app that inserts about 100k records into a database. I'm using EF core, like:
context.Users.AddRange(users);
await context.SaveChangesAsync();
I expected it to generate a simple insert query, like insert into users (...) values (...)
. But, this is the monstrosity it generated instead:
SET NOCOUNT ON;
DECLARE @inserted0 TABLE (..., [_Position] [int]);
MERGE [dbo].[Users] USING (
VALUES (@p0, @p1, @p2, @p3, @p4, 0),
(@p5, @p6, @p7, @p8, @p9, 1),
(@p10, @p11, @p12, @p13, @p14, 2)
...
) AS i (..., _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT (...)
VALUES (...)
OUTPUT INSERTED.[Id], i._Position
INTO @inserted0;
It did not even include all the items in one request. It ran the query multiple times, inserting about 3k records at a time. Obviously, this code is really slow. I found a similar question that suggested setting MaxBatchSize
or UseRelationalNulls
, but none of that helped in my case. Even if I decrease MaxBatchSize
to 1 (that would make it run 100k queries), EF still generates the same weird code. And in case I set MaxBatchSize
to 100k, it still generates batches with about 3k items at a time. So, it looks like MaxBatchSize
doesn't actually work in this case.
So, is there any way to make EF Core generate a faster insert code?
Upvotes: 0
Views: 1162
Reputation: 27416
EF Core is trying to generate insert query and return back generated ID's and other fields which are initialized by database, _Position
in your case. An there is no way to generate insert and forget statements via ChangeTracker.
I would suggest to use linq2db.EntityFrameworkCore (Disclaimer: I'm one of the creators)
await context.BulkCopyAsync(users);
100K records will be inserted approximately in 1 second.
Upvotes: 1