When entity model have byte[] PK, removing two or more entity from context and SaveChanges will throw exception.
Exception message: System.InvalidOperationException : Failed to compare two elements in the array.
---- System.ArgumentException : At least one object must implement IComparable.
Stack trace:
at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
at System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer)
at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IReadOnlyList`1 entries)+MoveNext()
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at efcore_test2.UnitTest1.Test1() in C:\Users\suzuki\Documents\Projects\efcore_test2\UnitTest1.cs:line 44
--- End of stack trace from previous location where exception was thrown ---
----- Inner Stack Trace -----
at System.Collections.Comparer.Compare(Object a, Object b)
at Microsoft.EntityFrameworkCore.Update.Internal.ModificationCommandComparer.Compare(ModificationCommand x, ModificationCommand y)
at System.Collections.Generic.ArraySortHelper`1.SwapIfGreater(T[] keys, Comparison`1 comparer, Int32 a, Int32 b)
at System.Collections.Generic.ArraySortHelper`1.IntrospectiveSort(T[] keys, Int32 left, Int32 length, Comparison`1 comparer)
at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
models:
```c#
public class Blog
{
[Key]
public byte[] Name { get; set; }
}
operation:
```c#
// ctx.Blogs have two or more entities
foreach (var blog in ctx.Blogs)
{
ctx.Remove(blog);
}
await ctx.SaveChangesAsync(); // This will fail with ASP.NET Core 2.1
full example: https://gitlab.com/suzuki_ds/efcore_test2
delete two row from DB.
await ctx.SaveChangesAsync() will throw exception.
EF Core version: Microsoft.AspNetCore.App 2.1.2
Database Provider: Microsoft.EntityFrameworkCore.Sqlite 2.1.1
Operating system: Windows 10 Pro 1803
IDE: Visual Studio 2017 15.7.5
Just ran into this myself. Glad to see it's being addressed. It's a bit of a project killer 馃槃
Is there a workaround in the meantime?
My (crude) workaround:
C#
using (var tx = await ctx.Database.BeginTransactionAsync())
{
foreach (var blog in ctx.Blogs.ToArray())
{
ctx.Remove(blog);
await ctx.SaveChangesAsync();
}
tx.Commit();
}
Thanks. In my case, though, the problem arises when multiple child records are being deleted when deleting a single parent record鈥攁ll of which have byte[] primary keys. Too many child tables for me to manually code 鈽癸笍 This seems like a pretty critical bug. I hope it gets fixed sooner rather than later.
So apparently one can create a custom IComparer function, but is it possible to hook that into EFCore?
I guess another approach is to add a converter to transform them into strings at the EFCore level. I'd prefer to avoid the extra cycles, but if that's the only way to move forward with my project, I guess that's what I'll have to do.
I just found SetValueComparer. Sorry for the chatter.
I found better workaround.
IComparer<ModificationCommand> that can handle byte[], by extending ModificationCommandComparerICompare<ModificationCommand> with defined implementation.https://gitlab.com/suzuki_ds/efcore_test2/commit/a275603bcc7f9ef7c3795ef4ba51b82da8aaf091
I think I should handle null case, but it worked with my test case.
THANK YOU! My custom comparer didn't fix the problem, but your solution did. Now I can move forward!
I found better workaround.
- define my own
IComparer<ModificationCommand>that can handlebyte[], by extendingModificationCommandComparer- replace
ICompare<ModificationCommand>with defined implementation.https://gitlab.com/suzuki_ds/efcore_test2/commit/a275603bcc7f9ef7c3795ef4ba51b82da8aaf091
I think I should handle null case, but it worked with my test case.
I didnt have an issue with something as complex as bytearray but it was using your comparer allowed me to find the culprit class where I needed to implement the comparer.
Thanks
I'm running into this issue as well. Not with deleting objects but with updating existing ones which happen to also have byte[] as PK. Does anyone have a copy at the above gitlab commits so I can have a gander?
I can't see how to subclass or add to the config the subclass of the ModificationCommandComparer. Googling it I just find a bunch of people going "Ah yeah, turns out C# can't compare byte[]. Changed it to an int/string and now it works!", but changing it isn't an option at this point.
Here's the code I used to make things work:
Thanks @Perlkonig, that helped a heap!
I am encountering this issue with an System.Net.IPAddress in my primary key using the Postgres inet type with NPGSQL. Maybe because IPAddress uses a ushort[] internally?
Most helpful comment
Here's the code I used to make things work:
https://github.com/AbstractPlay/api-aws/blob/master/sln/abstractplay/MyModificationCommandComparer.cs