The question
I have a filtered unique index defined as:
builder
.HasIndex(e => new { e.UserId, e.IsDefault })
.HasFilter($"[{nameof(MyEntity.IsDefault)}] = 1")
.IsUnique()
.HasDatabaseName($"UX_{nameof(MyEntity)}_{nameof(MyEntity.UserId)}_{nameof(MyEntity.IsDefault)}");
It translates into:
CREATE UNIQUE NONCLUSTERED INDEX [UX_MyEntity_UserId_IsDefault] ON [dbo].[MyEntity]
(
[UserId] ASC,
[IsDefault] ASC
)
WHERE ([IsDefault]=(1))
Because I can only have 1 record with the flag IsDefault set to true, the order of updates is important, I first need to set non default records to false, and then set the default one. Since the EF Core doesn't really know the condition/filter, it doesn't order the updates correclty and I often get an error with the message:
Cannot insert duplicate key row in object 'dbo.MyEntity' with unique index 'UX_MyEntity_UserId_IsDefault'.
Example
In the database I have:
| Id |
UserId |
IsDefault |
| 1 |
5 |
0 |
| 2 |
5 |
1 |
| 3 |
5 |
0 |
In the code we have a Parent and a Child object, the Parent has a collection of Children and it goes like this:
var parent = await parentEfRepository.GetByIdAsync(5, cancellationToken);
var newDefaultChild = parent.Children.Where(c => ...);
newDefaultChild.SetAsDefault();
await context.SaveChangesAsync(cancellationToken);
The SetAsDefault() method contains the logic to set other Children as non default and the result is that only 1 Child is marked as being the default one.
When we get to SaveChanges(), EF generates a SQL script that contains the updates which look like this:
SET NOCOUNT ON;
UPDATE [MyEntity] SET [IsDefault] = @p0
OUTPUT INSERTED.[PeriodEnd], INSERTED.[PeriodStart]
WHERE [Id] = @p1;
UPDATE [MyEntity] SET [IsDefault] = @p2
OUTPUT INSERTED.[PeriodEnd], INSERTED.[PeriodStart]
WHERE [Id] = @p3;
Because it's trying to update record with ID 1 first, we would end up with the following:
| Id |
UserId |
IsDefault |
| 1 |
5 |
1 |
| 2 |
5 |
1 |
| 3 |
5 |
0 |
And that's when the unique constraint kicks in and throws an error.
I have started experimenting with interceptors but it's getting dirty, surely someone had this issue before me?
Thanks!
Provider and version information
EF Core version: 8.0.11
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Windows
IDE: Rider
The question
I have a filtered unique index defined as:
It translates into:
Because I can only have 1 record with the flag
IsDefaultset totrue, the order of updates is important, I first need to set non default records tofalse, and then set the default one. Since the EF Core doesn't really know the condition/filter, it doesn't order the updates correclty and I often get an error with the message:Example
In the database I have:
In the code we have a
Parentand aChildobject, theParenthas a collection ofChildren and it goes like this:The
SetAsDefault()method contains the logic to set otherChildren as non default and the result is that only 1Childis marked as being the default one.When we get to
SaveChanges(), EF generates a SQL script that contains the updates which look like this:Because it's trying to update record with ID 1 first, we would end up with the following:
And that's when the unique constraint kicks in and throws an error.
I have started experimenting with interceptors but it's getting dirty, surely someone had this issue before me?
Thanks!
Provider and version information
EF Core version: 8.0.11
Database provider:
Microsoft.EntityFrameworkCore.SqlServerTarget framework: .NET 8.0
Operating system: Windows
IDE: Rider