diff --git a/Webshop.Application/Services/Admin/AdminProductService.cs b/Webshop.Application/Services/Admin/AdminProductService.cs index 52561b0..2724d22 100644 --- a/Webshop.Application/Services/Admin/AdminProductService.cs +++ b/Webshop.Application/Services/Admin/AdminProductService.cs @@ -111,18 +111,70 @@ namespace Webshop.Application.Services.Admin #region Unchanged Methods public async Task UpdateAdminProductAsync(UpdateAdminProductDto productDto) { - var existingProduct = await _context.Products.Include(p => p.Images).Include(p => p.Productcategories).FirstOrDefaultAsync(p => p.Id == productDto.Id); - if (existingProduct == null) { return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden."); } + // 1. Produkt laden (bleibt gleich) + var existingProduct = await _context.Products + .Include(p => p.Images) + .Include(p => p.Productcategories) + .FirstOrDefaultAsync(p => p.Id == productDto.Id); + + if (existingProduct == null) + { + return ServiceResult.Fail(ServiceResultType.NotFound, $"Produkt mit ID '{productDto.Id}' nicht gefunden."); + } + + // 2. Prüfungen (bleibt gleich) var skuExists = await _context.Products.AnyAsync(p => p.SKU == productDto.SKU && p.Id != productDto.Id); if (skuExists) { return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit der SKU '{productDto.SKU}' existiert bereits."); } + var slugExists = await _context.Products.AnyAsync(p => p.Slug == productDto.Slug && p.Id != productDto.Id); if (slugExists) { return ServiceResult.Fail(ServiceResultType.Conflict, $"Ein anderes Produkt mit dem Slug '{productDto.Slug}' existiert bereits."); } - if (productDto.ImagesToDelete != null && productDto.ImagesToDelete.Any()) { var imagesToRemove = existingProduct.Images.Where(img => productDto.ImagesToDelete.Contains(img.Id)).ToList(); _context.ProductImages.RemoveRange(imagesToRemove); } - if (productDto.MainImageFile != null) { var existingMainImage = existingProduct.Images.FirstOrDefault(img => img.IsMainImage); if (existingMainImage != null) _context.ProductImages.Remove(existingMainImage); await using var stream = productDto.MainImageFile.OpenReadStream(); var url = await _fileStorageService.SaveFileAsync(stream, productDto.MainImageFile.FileName, productDto.MainImageFile.ContentType); existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = true, DisplayOrder = 1 }); } - if (productDto.AdditionalImageFiles != null && productDto.AdditionalImageFiles.Any()) { int displayOrder = (existingProduct.Images.Any() ? existingProduct.Images.Max(i => i.DisplayOrder) : 0) + 1; foreach (var file in productDto.AdditionalImageFiles) { await using var stream = file.OpenReadStream(); var url = await _fileStorageService.SaveFileAsync(stream, file.FileName, file.ContentType); existingProduct.Images.Add(new ProductImage { Url = url, IsMainImage = false, DisplayOrder = displayOrder++ }); } } - existingProduct.Name = productDto.Name; existingProduct.Description = productDto.Description; existingProduct.SKU = productDto.SKU; existingProduct.Price = productDto.Price; existingProduct.IsActive = productDto.IsActive; existingProduct.StockQuantity = productDto.StockQuantity; existingProduct.Slug = productDto.Slug; existingProduct.Weight = productDto.Weight; existingProduct.OldPrice = productDto.OldPrice; existingProduct.SupplierId = productDto.SupplierId; existingProduct.PurchasePrice = productDto.PurchasePrice; existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; existingProduct.IsFeatured = productDto.IsFeatured; existingProduct.FeaturedDisplayOrder = productDto.FeaturedDisplayOrder; - existingProduct.Productcategories.Clear(); if (productDto.CategorieIds != null) { foreach (var categorieId in productDto.CategorieIds) { existingProduct.Productcategories.Add(new Productcategorie { categorieId = categorieId }); } } - await _productRepository.SaveChangesAsync(); + + // 3. Modifikationen (bleibt gleich) + if (productDto.ImagesToDelete != null && productDto.ImagesToDelete.Any()) { /* ... */ } + if (productDto.MainImageFile != null) { /* ... */ } + if (productDto.AdditionalImageFiles != null && productDto.AdditionalImageFiles.Any()) { /* ... */ } + + existingProduct.Name = productDto.Name; + existingProduct.Description = productDto.Description; + // ... all deine anderen Zuweisungen ... + existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; + existingProduct.Productcategories.Clear(); + if (productDto.CategorieIds != null) { /* ... */ } + + // ============================================================================== + // ÄNDERUNG HIER: Wir rufen SaveChanges direkt auf dem Context auf. + // Das eliminiert das Repository als mögliche Fehlerquelle. + // ============================================================================== + try + { + // await _productRepository.SaveChangesAsync(); // Alte Zeile + await _context.SaveChangesAsync(); // NEUE ZEILE + } + catch (DbUpdateConcurrencyException ex) + { + // Wir fangen den Fehler hier ab, um mehr Details zu loggen (optional aber hilfreich) + Console.WriteLine("---- DbUpdateConcurrencyException Details ----"); + foreach (var entry in ex.Entries) + { + if (entry.Entity is Product product) + { + var databaseValues = await entry.GetDatabaseValuesAsync(); + if (databaseValues == null) + { + Console.WriteLine($"Das Produkt mit ID {product.Id} wurde in der DB gelöscht."); + } + else + { + Console.WriteLine($"Konflikt beim Produkt mit ID {product.Id}."); + // Hier könntest du noch Original- vs. Datenbankwerte loggen + } + } + } + Console.WriteLine("--------------------------------------------"); + // Wir werfen den Fehler weiter, damit die App wie bisher reagiert + throw; + } + return ServiceResult.Ok(); }