Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BulkInsertOrUpdateOrDelete: bulkConfig.StatsInfo.StatsNumberInserted is wrong if nothing was updated #666

Closed
michull opened this issue Nov 26, 2021 · 4 comments
Labels

Comments

@michull
Copy link

michull commented Nov 26, 2021

I'm using EFCore.BulkExtensions v6.0.7 and initialize my table on a MS SQL server with

context.Students.BatchDelete();
context.Students.Add(new Student() { StudentId = 1, Name = "John Doe" });
context.SaveChanges();

Later I'm using the following code to sync the data (in fact there is no difference):

List<Student> students = new() { new Student() { StudentId = 1, Name = "John Doe" }};
BulkConfig bulkConfig = new() { CalculateStats = true };
context.BulkInsertOrUpdateOrDelete(students, bulkConfig);

I would expect that all statistic numbers are 0, but the lines

Console.WriteLine($"Deleted={bulkConfig.StatsInfo.StatsNumberDeleted}");
Console.WriteLine($"Inserted={bulkConfig.StatsInfo.StatsNumberInserted}");
Console.WriteLine($"Updated={bulkConfig.StatsInfo.StatsNumberUpdated}");

output

Deleted=0
Inserted=1
Updated=0

The generated merge statement was as follows

MERGE [dbo].[Students] WITH (HOLDLOCK) AS T
USING (SELECT TOP 1 * FROM [dbo].[StudentsTemp9c324527] ORDER BY [StudentId]) AS S 
ON T.[StudentId] = S.[StudentId] 
WHEN NOT MATCHED BY TARGET THEN INSERT ([Name]) VALUES (S.[Name]) 
WHEN MATCHED AND EXISTS (SELECT S.[Name] EXCEPT SELECT T.[Name]) THEN UPDATE SET T.[Name] = S.[Name] 
WHEN NOT MATCHED BY SOURCE THEN DELETE 
OUTPUT COALESCE(INSERTED.[StudentId], DELETED.[StudentId]), COALESCE(INSERTED.[Name], DELETED.[Name]),(CASE $action WHEN 'UPDATE' THEN 1 Else 0 END),(CASE $action WHEN 'DELETE' THEN 1 Else 0 END) INTO [dbo].[StudentsTemp9c324527Output];

the class TableInfo has the following lines:

int totalNumber = entities.Count;
int numberUpdated = [...]
int numberDeleted = [...]
BulkConfig.StatsInfo = new StatsInfo
{
    StatsNumberUpdated = numberUpdated,
    StatsNumberDeleted = numberDeleted,
    StatsNumberInserted = totalNumber - numberUpdated - numberDeleted
};
StatsNumberInserted = totalNumber - numberUpdated - numberDeleted

I think, the number of inserted records cannot be calculated in this way, because of the additional statement AND EXISTS (SELECT S.[Name] EXCEPT SELECT T.[Name]) after WHEN MATCHED.

@borisdj
Copy link
Owner

borisdj commented Nov 26, 2021

Will look into it.

@carltstein
Copy link

I am experiencing the same issue.

StatsInfo.StatsNumberInserted counts rows inserted AND rows where no changes were made.

I'm using:
Microsoft.EntityFrameworkCore.SqlServer 6.0.13
EFCore.BulkExtensions.SqlServer 6.6.5

p.s. Thanks @borisdj for an awesome library that has shaved literally hours off of some of my sync operations

@palhal
Copy link

palhal commented Mar 20, 2023

This also happens when I don't want to update existing rows:

context.BulkInsertOrUpdate(items, new BulkConfig
{
    PropertiesToIncludeOnUpdate = new() { string.Empty },
    CalculateStats = true
});

Actual affected rows are 0, but since StatsNumberUpdated is 0 (correct), StatsNumberInserted is incorrectly set to items.Count.

@borisdj
Copy link
Owner

borisdj commented May 26, 2023

Fixed with latest v.

@borisdj borisdj closed this as completed May 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants