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:
@@ -44,7 +44,7 @@ public class EntityDataContext : ExtendedTypeContext
|
||||
modelBuilder.Entity<Model.AttributeDefinition>();
|
||||
modelBuilder.Entity<ConnectServerDefinition>();
|
||||
modelBuilder.Entity<ChatServerDefinition>();
|
||||
modelBuilder.Entity<MiniGameRankingEntry>();
|
||||
modelBuilder.Entity<MiniGameRankingEntry>().Apply();
|
||||
modelBuilder.Entity<GameServerDefinition>(entity =>
|
||||
{
|
||||
entity.Property(e => e.PvpEnabled).HasDefaultValue(true);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
5293
src/Persistence/EntityFramework/Migrations/20260723073950_CascadeDeleteMiniGameRankingEntries.Designer.cs
generated
Normal file
5293
src/Persistence/EntityFramework/Migrations/20260723073950_CascadeDeleteMiniGameRankingEntries.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
// <copyright file="20260723073950_CascadeDeleteMiniGameRankingEntries.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations
|
||||
{
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class CascadeDeleteMiniGameRankingEntries : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_Character_CharacterId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_MiniGameDefinition_MiniGameId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_Character_CharacterId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry",
|
||||
column: "CharacterId",
|
||||
principalSchema: "data",
|
||||
principalTable: "Character",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_MiniGameDefinition_MiniGameId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry",
|
||||
column: "MiniGameId",
|
||||
principalSchema: "config",
|
||||
principalTable: "MiniGameDefinition",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_Character_CharacterId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_MiniGameDefinition_MiniGameId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_Character_CharacterId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry",
|
||||
column: "CharacterId",
|
||||
principalSchema: "data",
|
||||
principalTable: "Character",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_MiniGameRankingEntry_MiniGameDefinition_MiniGameId",
|
||||
schema: "data",
|
||||
table: "MiniGameRankingEntry",
|
||||
column: "MiniGameId",
|
||||
principalSchema: "config",
|
||||
principalTable: "MiniGameDefinition",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4627,11 +4627,13 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations
|
||||
{
|
||||
b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "RawCharacter")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId");
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", "RawMiniGame")
|
||||
.WithMany()
|
||||
.HasForeignKey("MiniGameId");
|
||||
.HasForeignKey("MiniGameId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("RawCharacter");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user