Bug description
Regression bug starting with <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.3" /> (it was fine on "10.0.2" and prior).
I'm not quite sure which specific part of the model configuration trigger the bug to happen, but it became impossible to save my entities to the database now as it falsely see a discriminator as being modified while it is "init-only".
It's also weird because it says BeatLeaderScore.Discriminator, but it's not an existing flied in the migration:
Since the issue occurred from 10.0.3, it might be easier to track commits between 10.0.2 and 10.0.3 that might have impacted the regression.
Here is a strip down version of my model configuration:
public abstract class AbstractScore
{
public int Id { get; set; }
public required DateTimeOffset SetAt { get; init; }
public required int? MaxCombo { get; init; }
public required bool IsFullCombo { get; init; }
public EScoreType Type { get; private init; }
public enum EScoreType : byte { ScoreSaber = 0, BeatLeader = 1 }
}
public class AbstractScoreConfiguration : IEntityTypeConfiguration<AbstractScore>
{
public void Configure(EntityTypeBuilder<AbstractScore> builder)
{
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.ValueGeneratedOnAdd();
builder.HasIndex(x => x.SetAt);
builder.HasDiscriminator(x => x.Type)
.HasValue<ScoreSaberScore>(AbstractScore.EScoreType.ScoreSaber)
.HasValue<BeatLeaderScore>(AbstractScore.EScoreType.BeatLeader)
.IsComplete();
}
}
public sealed class BeatLeaderScore : AbstractScore
{
public required int? BeatLeaderScoreId { get; init; }
public required ScoreStatistics? Statistics { get; init; }
}
public class BeatLeaderScoreConfiguration : IEntityTypeConfiguration<BeatLeaderScore>
{
public void Configure(EntityTypeBuilder<BeatLeaderScore> builder)
{
builder.HasBaseType<AbstractScore>();
builder.ComplexProperty(x => x.Statistics).Configure(new ScoreStatisticConfiguration());
}
}
public class ScoreStatistics
{
public WinTracker WinTracker { get; set; } = null!;
public HitTracker HitTracker { get; set; } = null!;
public AccuracyTracker AccuracyTracker { get; set; } = null!;
public ScoreGraphTracker ScoreGraphTracker { get; set; } = null!;
}
public class ScoreStatisticConfiguration : IComplexPropertyConfiguration<ScoreStatistics>
{
public ComplexPropertyBuilder<ScoreStatistics> Configure(ComplexPropertyBuilder<ScoreStatistics> builder)
{
builder.HasDiscriminator();
builder.ComplexProperty(x => x.WinTracker)
.ComplexProperty(x => x.AverageHeadPosition);
builder.ComplexProperty(x => x.HitTracker);
builder.ComplexProperty(x => x.AccuracyTracker);
builder.ComplexProperty(x => x.ScoreGraphTracker);
return builder;
}
}
public readonly record struct AverageHeadPosition(float X, float Y, float Z);
public record WinTracker(
bool IsWin,
float EndTime,
int PauseCount,
float TotalPauseDuration,
int TotalScore,
int MaxScore
)
{
public required AverageHeadPosition? AverageHeadPosition { get; init; }
}
public record HitTracker(int Max115Streak, float LeftTiming, float RightTiming,);
public record AccuracyTracker(
float AccRight,
float AccLeft,
IReadOnlyList<float> LeftAverageCutGraphGrid,
IReadOnlyList<float> RightAverageCutGraphGrid,
IReadOnlyList<float> AccuracyGrid
);
public record ScoreGraphTracker(List<float> Graph);
public interface IComplexPropertyConfiguration<T> where T : notnull
{
public ComplexPropertyBuilder<T> Configure(ComplexPropertyBuilder<T> builder);
}
public static class EFCoreComplexPropertyConfigurationExtensions
{
public static ComplexPropertyBuilder<T> Configure<T>(
this ComplexPropertyBuilder<T> self, IComplexPropertyConfiguration<T> builder) where T : notnull
=> builder.Configure(self);
}
public sealed class ScoreSaberScore : AbstractScore
{
public required int ScoreSaberScoreId { get; init; }
public required string? DeviceHmd { get; init; }
public required string? DeviceControllerLeft { get; init; }
public required string? DeviceControllerRight { get; init; }
}
public class ScoreSaberScoreConfiguration : IEntityTypeConfiguration<ScoreSaberScore>
{
public void Configure(EntityTypeBuilder<ScoreSaberScore> builder)
{
builder.HasBaseType<AbstractScore>();
builder.Property(x => x.DeviceHmd).HasMaxLength(30);
builder.Property(x => x.DeviceControllerLeft).HasMaxLength(20);
builder.Property(x => x.DeviceControllerRight).HasMaxLength(20);
}
}
Your code
The full code of my project can be seen under this repository:
https://github.com/GuildSaber/GuildSaber
Stack traces
fail: Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'GuildSaber.Database.Contexts.Server.ServerDbContext'.
System.InvalidOperationException: The property 'BeatLeaderScore.Discriminator' is defined as read-only after it has been saved, but its value has been modified or marked as modified.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntryBase.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass30_0`2.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
System.InvalidOperationException: The property 'BeatLeaderScore.Discriminator' is defined as read-only after it has been saved, but its value has been modified or marked as modified.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntryBase.PrepareToSave()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.GetEntriesToSave(Boolean cascadeChanges)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass30_0`2.<<ExecuteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func`4 operation, Func`4 verifySucceeded, TState state, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Verbose output
EF Core version
10.0.3
Database provider
No response
Target framework
.NET 10
Operating system
Linux
IDE
No response
Bug description
Regression bug starting with
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.3" />(it was fine on"10.0.2"and prior).I'm not quite sure which specific part of the model configuration trigger the bug to happen, but it became impossible to save my entities to the database now as it falsely see a discriminator as being modified while it is "init-only".
It's also weird because it says
BeatLeaderScore.Discriminator, but it's not an existing flied in the migration:Since the issue occurred from 10.0.3, it might be easier to track commits between 10.0.2 and 10.0.3 that might have impacted the regression.
Here is a strip down version of my model configuration:
Your code
Stack traces
Verbose output
EF Core version
10.0.3
Database provider
No response
Target framework
.NET 10
Operating system
Linux
IDE
No response