Cascade delete mini game ranking entries

A character which has participated in a mini game with enabled ranking
statistics could not be deleted. The database rejected it with

  Npgsql.PostgresException 23503: update or delete on table "Character"
  violates foreign key constraint "FK_MiniGameRankingEntry_Character_CharacterId"

MiniGameRankingEntry is a member of neither the character nor the mini game
definition aggregate, so the model generator doesn't emit a delete behavior
for its references. The entity was registered without any additional
configuration, so Entity Framework fell back to no action for these optional
relationships, which the database translates into a restricting foreign key.

Both references are now configured with a cascading delete, matching the
behavior of the other references to a character. With that, all seven
relationships pointing to Character delete their dependents.

This affects both deletion paths: the one of the player
(DeleteCharacterAction) and the one of an administrator, who can remove a
character from the account in the admin panel.

The cascade on the mini game definition is deliberate and part of the same
defect: deleting a definition in the admin panel ran into the very same
restriction. It does mean that the ranking history of a mini game is dropped
together with its definition.

Reported in issue 796.

(cherry picked from commit a391bdedbbd3a36ccb37124125bd7ede4f851dca)
This commit is contained in:
nolt
2026-07-23 10:31:32 +02:00
committed by Acentech Dev
parent d03518deac
commit 9e0bcede2f
5 changed files with 5396 additions and 3 deletions

View File

@@ -4,6 +4,7 @@
namespace MUnique.OpenMU.Persistence.EntityFramework.Extensions.ModelBuilder;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using MUnique.OpenMU.Persistence.EntityFramework.Model;
@@ -41,4 +42,21 @@ internal static class MiniGameExtensions
builder.Property(p => p.Name).HasConversion(LocalizedStringConverter.Instance);
builder.Property(p => p.Description).HasConversion(LocalizedStringConverter.Instance);
}
/// <summary>
/// Applies the settings for the <see cref="MiniGameRankingEntry"/> entity.
/// </summary>
/// <param name="builder">The builder.</param>
/// <remarks>
/// A ranking entry is a member of neither the character's nor the mini game definition's
/// aggregate. The model generator only emits a cascading delete for navigations marked with
/// <see cref="MUnique.OpenMU.DataModel.Composition.MemberOfAggregateAttribute"/>, so these two
/// references are left without a delete behavior; Entity Framework then defaults to no action
/// for them, and the database refuses to delete a character which has played a mini game.
/// </remarks>
public static void Apply(this EntityTypeBuilder<MiniGameRankingEntry> builder)
{
builder.HasOne(entry => entry.RawCharacter).WithMany().OnDelete(DeleteBehavior.Cascade);
builder.HasOne(entry => entry.RawMiniGame).WithMany().OnDelete(DeleteBehavior.Cascade);
}
}