fix(gift): tutorial gift_receive ThenIncludes OwnedItemEntry.Item
Same project_ef_nav_include_pitfall as 27ebb51's tutorial pack_open fix but in the gift path: without .ThenInclude(i => i.Item), the existing OwnedItemEntry's Item nav defaults to a new ItemEntry() (Id=0), so RewardGrantService.ApplyAsync's `FirstOrDefault(i => i.Item.Id == detailId)` misses pre-existing rows. It falls through to add a new entry, and the (ViewerId, ItemId) unique index added 2026-05-25 throws on SaveChanges → 500 to the client, no tutorial advancement, no currency grant. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -77,8 +77,14 @@ public class GiftController : SVSimController
|
||||
// the pattern in TutorialController.Update and to make the intent clear.
|
||||
// AsSplitQuery is the default-safe pattern when including viewer collections
|
||||
// (project memory: project_ef_split_query).
|
||||
//
|
||||
// ThenInclude(i => i.Item) is load-bearing: OwnedItemEntry.Item is a separate non-owned
|
||||
// entity whose default initialiser is `new ItemEntry()` (Id=0). Without the explicit
|
||||
// ThenInclude, RewardGrantService.ApplyAsync's `FirstOrDefault(i => i.Item.Id == ...)`
|
||||
// never matches a pre-existing row → falls through to add a duplicate → (ViewerId, ItemId)
|
||||
// unique index throws on SaveChanges (project_ef_nav_include_pitfall).
|
||||
var viewer = await _db.Viewers
|
||||
.Include(v => v.Items)
|
||||
.Include(v => v.Items).ThenInclude(i => i.Item)
|
||||
.Include(v => v.MissionData)
|
||||
.AsSplitQuery()
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
|
||||
Reference in New Issue
Block a user