From 8d1e3d153c3be6a213043403022a69e4d36519be Mon Sep 17 00:00:00 2001 From: SysAdmin Date: Sun, 5 Oct 2025 16:33:33 +0100 Subject: [PATCH] Fix: Manually load ProductVariants with separate query instead of Include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Root Cause**: EF Core Include() was not properly materializing the Variants navigation property despite correct SQL JOIN generation. **Solution**: Load variants separately and manually group by ProductId for DTO mapping. This bypasses EF Core's navigation property fixup issues. 🤖 Generated with Claude Code Co-Authored-By: Claude --- LittleShop/Services/ProductService.cs | 80 +++++++++++++++++---------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/LittleShop/Services/ProductService.cs b/LittleShop/Services/ProductService.cs index f03d515..9a09ca8 100644 --- a/LittleShop/Services/ProductService.cs +++ b/LittleShop/Services/ProductService.cs @@ -22,11 +22,19 @@ public class ProductService : IProductService .Include(p => p.Category) .Include(p => p.Photos) .Include(p => p.MultiBuys) - .Include(p => p.Variants) .Where(p => p.IsActive) - .AsSplitQuery() .ToListAsync(); + // Manually load variants for all products + var productIds = products.Select(p => p.Id).ToList(); + var allVariants = await _context.ProductVariants + .Where(v => productIds.Contains(v.ProductId)) + .ToListAsync(); + + // Group variants by ProductId for quick lookup + var variantsByProduct = allVariants.GroupBy(v => v.ProductId) + .ToDictionary(g => g.Key, g => g.ToList()); + return products.Select(p => new ProductDto { Id = p.Id, @@ -63,19 +71,21 @@ public class ProductService : IProductService CreatedAt = mb.CreatedAt, UpdatedAt = mb.UpdatedAt }).ToList(), - Variants = p.Variants.Select(v => new ProductVariantDto - { - Id = v.Id, - ProductId = v.ProductId, - VariantType = v.VariantType, - Name = v.Name, - Price = v.Price, - StockLevel = v.StockLevel, - SortOrder = v.SortOrder, - IsActive = v.IsActive, - CreatedAt = v.CreatedAt, - UpdatedAt = v.UpdatedAt - }).ToList() + Variants = variantsByProduct.ContainsKey(p.Id) + ? variantsByProduct[p.Id].Select(v => new ProductVariantDto + { + Id = v.Id, + ProductId = v.ProductId, + VariantType = v.VariantType, + Name = v.Name, + Price = v.Price, + StockLevel = v.StockLevel, + SortOrder = v.SortOrder, + IsActive = v.IsActive, + CreatedAt = v.CreatedAt, + UpdatedAt = v.UpdatedAt + }).ToList() + : new List() }).ToList(); } @@ -85,11 +95,19 @@ public class ProductService : IProductService .Include(p => p.Category) .Include(p => p.Photos) .Include(p => p.MultiBuys) - .Include(p => p.Variants) .Where(p => p.IsActive && p.CategoryId == categoryId) - .AsSplitQuery() .ToListAsync(); + // Manually load variants for all products + var productIds = products.Select(p => p.Id).ToList(); + var allVariants = await _context.ProductVariants + .Where(v => productIds.Contains(v.ProductId)) + .ToListAsync(); + + // Group variants by ProductId for quick lookup + var variantsByProduct = allVariants.GroupBy(v => v.ProductId) + .ToDictionary(g => g.Key, g => g.ToList()); + return products.Select(p => new ProductDto { Id = p.Id, @@ -126,19 +144,21 @@ public class ProductService : IProductService CreatedAt = mb.CreatedAt, UpdatedAt = mb.UpdatedAt }).ToList(), - Variants = p.Variants.Select(v => new ProductVariantDto - { - Id = v.Id, - ProductId = v.ProductId, - VariantType = v.VariantType, - Name = v.Name, - Price = v.Price, - StockLevel = v.StockLevel, - SortOrder = v.SortOrder, - IsActive = v.IsActive, - CreatedAt = v.CreatedAt, - UpdatedAt = v.UpdatedAt - }).ToList() + Variants = variantsByProduct.ContainsKey(p.Id) + ? variantsByProduct[p.Id].Select(v => new ProductVariantDto + { + Id = v.Id, + ProductId = v.ProductId, + VariantType = v.VariantType, + Name = v.Name, + Price = v.Price, + StockLevel = v.StockLevel, + SortOrder = v.SortOrder, + IsActive = v.IsActive, + CreatedAt = v.CreatedAt, + UpdatedAt = v.UpdatedAt + }).ToList() + : new List() }).ToList(); }