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

BulkInsertOrUpdate with custom UpdateBy columns does not set output identity when UpdateBy columns are not unique in database #48

Closed
videokojot opened this issue Oct 15, 2023 · 0 comments

Comments

@videokojot
Copy link
Owner

tracking issue for:
https://github.com/borisdj/EFCore.BulkExtensions/issues/1251

copy of the issue:

When using BulkInsertOrUpdate with custom UpdateBy columns and SetOutputIdentity && PreserveInsertOrder does not work when some of the UpdateByColums values contains null values. See example:

    [Fact]
    public void BulkInsertOrUpdate_WillNotAssignIds_OnDuplicateValues()
    {
        var bulkId = Guid.NewGuid();
        var existingItemId = "existingDuplicateOf";

        var initialItem = new Item2()
        {
            StringProperty = existingItemId,
            Name = "initial1",
            BulkIdentifier = bulkId,
        };

        var duplicateInitial = new Item2()
        {
            StringProperty = existingItemId,
            Name = "initial2",
            BulkIdentifier = bulkId,
        };

        using (var db = GetTestContext())
        {
            db.Items2.Add(initialItem);
            db.Items2.Add(duplicateInitial);
            db.SaveChanges();

            Assert.NotEqual(0, initialItem.Id);
            Assert.NotEqual(0, duplicateInitial.Id);
        }

        using (var db = GetTestContext())
        {
            var newItem = new Item2()
            {
                StringProperty = "A",
                BulkIdentifier = bulkId,
            };

            var updatedItem = new Item2()
            {
                StringProperty = existingItemId, // begins with 'e'
                BulkIdentifier = bulkId,
                Name = "updated by Bulks",
            };

            var newItem2 = new Item2()
            {
                StringProperty = "ZZZ_will not get assigned",
                BulkIdentifier = bulkId,
            };

            var ensureList = new[] { newItem, updatedItem, newItem2 };

            // bug in library - which for some orders will not set some output ids:
            db.BulkInsertOrUpdate(ensureList,
                c =>
                {
                    c.UpdateByProperties = new []{ nameof(Item2.StringProperty), nameof(Item2.BulkIdentifier) };
                    c.SetOutputIdentity = true;
                    c.PreserveInsertOrder = true;
                });

            // Since newItem2 is last in the list (of ordered by StringProp), it will not get assigned the id because of the bug (see above)...
            Assert.Equal(0, newItem2.Id);

            // The items were inserted/updated
            Assert.Equal(4, GetItems2BulkCount(bulkId));
        }
    }

Why does this happen? This happens due to bug in

https://github.com/borisdj/EFCore.BulkExtensions/blob/89e3ff2d0516a0d1e640758830b9826a451228e5/EFCore.BulkExtensions/TableInfo.cs#L1079

in method UpdateEntitiesIdentity where the output setting cycle is going only trough

https://github.com/borisdj/EFCore.BulkExtensions/blob/89e3ff2d0516a0d1e640758830b9826a451228e5/EFCore.BulkExtensions/TableInfo.cs#L1078

so if there are duplicate values in output values (duplicate in sense of custom UpdateByColumns), then some of the ids of the items might not be set. See example above for specific scenario.
Proposed solution:

  1. Just add post check that will verify that all ids were set (are not negative or zero). And if this would be detected, then it which will throw or inform user that the output ids are not reliable.
  2. Fix the code in UpdateEntitiesIdentity, and ensure that id for all items is set, by looping trough the input entities instead of the entities from input table. The only question is then which ids (of possibly mulltiple ids shoudl be set to input entity - first, last ...?)

I will happily create PR with nice tests if you agree (we already do have something simmilar in our fork).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant