From 4fbcd0dc11e4a3c57c95faac0a7602669f259af9 Mon Sep 17 00:00:00 2001 From: OlgunR Date: Mon, 19 Jan 2026 11:10:19 +0100 Subject: [PATCH] Support selecting update procedure for catalog updates Added CatalogUpdateProcedure enum to domain. CatalogWriteDto now includes UpdateProcedure property in both application and BlazorWasm layers. Catalogs.razor form allows users to choose between PRTBMY_CATALOG_UPDATE and PRTBMY_CATALOG_SAVE when editing. Repository, service, and handler layers updated to pass and use the selected procedure. Default remains Update. Updated comments and TODOs for clarity and future refactoring. --- DbFirst.API/Controllers/CatalogsController.cs | 4 ---- .../Catalogs/CatalogService.cs | 22 ++++-------------- .../Catalogs/CatalogWriteDto.cs | 3 +++ .../Catalogs/Commands/UpdateCatalogHandler.cs | 4 +++- DbFirst.BlazorWasm/Models/CatalogWriteDto.cs | 1 + DbFirst.BlazorWasm/Pages/Catalogs.razor | 16 ++++++++++++- DbFirst.Domain/CatalogUpdateProcedure.cs | 7 ++++++ .../Repositories/ICatalogRepository.cs | 23 ++----------------- .../Repositories/CatalogRepository.cs | 9 ++++++-- 9 files changed, 43 insertions(+), 46 deletions(-) create mode 100644 DbFirst.Domain/CatalogUpdateProcedure.cs diff --git a/DbFirst.API/Controllers/CatalogsController.cs b/DbFirst.API/Controllers/CatalogsController.cs index 2b13d46..b2b0f66 100644 --- a/DbFirst.API/Controllers/CatalogsController.cs +++ b/DbFirst.API/Controllers/CatalogsController.cs @@ -54,10 +54,6 @@ public class CatalogsController : ControllerBase { return NotFound(); } - if (!string.Equals(current.CatTitle, dto.CatTitle, StringComparison.Ordinal)) - { - return BadRequest("CatTitle cannot be changed."); - } var updated = await _mediator.Send(new UpdateCatalogCommand(id, dto), cancellationToken); if (updated == null) diff --git a/DbFirst.Application/Catalogs/CatalogService.cs b/DbFirst.Application/Catalogs/CatalogService.cs index 65a4946..dbf5904 100644 --- a/DbFirst.Application/Catalogs/CatalogService.cs +++ b/DbFirst.Application/Catalogs/CatalogService.cs @@ -1,27 +1,14 @@ using AutoMapper; using DbFirst.Domain.Repositories; using DbFirst.Domain.Entities; +using DbFirst.Domain; namespace DbFirst.Application.Catalogs; //TODO: create generic service to reduce code duplication - -/* Copilot's Response: -A generic CRUD base service adds little value in your case: - -Pros: -• Less boilerplate for simple entities without special logic. -• Uniform CRUD signatures. - -Cons/Practical here: -• Domain logic differs per entity(unique title check, setting audit fields, forbidding title changes, stored procs with output GUID). -• Generic services tend to be diluted by virtual methods/hooks for special cases—ending up with per-entity overrides and little real gain. -• With stored procedures and output parameters, the pattern doesn’t fit cleanly because operations aren’t symmetric (separate procs for insert/update/delete). - -Conclusion: For this solution a generic service would be more overhead than benefit. If you later have multiple very similar entities without special logic, -you could consider a lightweight generic interface/base; for now, the specialized service implementation is cleaner. */ - +//TODO (TR): kod tekrar?n? azaltmak için generic bir servis/basit CRUD altyap?s? ekleyin //TODO: implement CQRS pattern with MediatR +//TODO (TR): CQRS desenini MediatR ile uygulay?n public class CatalogService : ICatalogService { private readonly ICatalogRepository _repository; @@ -79,7 +66,8 @@ public class CatalogService : ICatalogService entity.ChangedWho = "system"; entity.ChangedWhen = DateTime.UtcNow; - var updated = await _repository.UpdateAsync(id, entity, cancellationToken); + var procedure = dto.UpdateProcedure; + var updated = await _repository.UpdateAsync(id, entity, procedure, cancellationToken); return updated == null ? null : _mapper.Map(updated); } diff --git a/DbFirst.Application/Catalogs/CatalogWriteDto.cs b/DbFirst.Application/Catalogs/CatalogWriteDto.cs index 8d122f6..c95e39c 100644 --- a/DbFirst.Application/Catalogs/CatalogWriteDto.cs +++ b/DbFirst.Application/Catalogs/CatalogWriteDto.cs @@ -1,7 +1,10 @@ +using DbFirst.Domain; + namespace DbFirst.Application.Catalogs; public class CatalogWriteDto { public string CatTitle { get; set; } = null!; public string CatString { get; set; } = null!; + public CatalogUpdateProcedure UpdateProcedure { get; set; } = CatalogUpdateProcedure.Update; } diff --git a/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs b/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs index 7f54733..016e438 100644 --- a/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs +++ b/DbFirst.Application/Catalogs/Commands/UpdateCatalogHandler.cs @@ -1,6 +1,7 @@ using AutoMapper; using DbFirst.Domain.Entities; using DbFirst.Domain.Repositories; +using DbFirst.Domain; using MediatR; namespace DbFirst.Application.Catalogs.Commands; @@ -32,7 +33,8 @@ public class UpdateCatalogHandler : IRequestHandler(updated); } } diff --git a/DbFirst.BlazorWasm/Models/CatalogWriteDto.cs b/DbFirst.BlazorWasm/Models/CatalogWriteDto.cs index c9197fa..54b7a95 100644 --- a/DbFirst.BlazorWasm/Models/CatalogWriteDto.cs +++ b/DbFirst.BlazorWasm/Models/CatalogWriteDto.cs @@ -4,4 +4,5 @@ public class CatalogWriteDto { public string CatTitle { get; set; } = string.Empty; public string CatString { get; set; } = string.Empty; + public int UpdateProcedure { get; set; } = 0; // 0 = Update, 1 = Save } diff --git a/DbFirst.BlazorWasm/Pages/Catalogs.razor b/DbFirst.BlazorWasm/Pages/Catalogs.razor index d9acb35..0486b46 100644 --- a/DbFirst.BlazorWasm/Pages/Catalogs.razor +++ b/DbFirst.BlazorWasm/Pages/Catalogs.razor @@ -32,6 +32,19 @@ else if (!string.IsNullOrWhiteSpace(infoMessage)) + @if (isEditing) + { +
+
+ + + + + +
+
+ } +
@@ -132,7 +145,8 @@ else formModel = new CatalogWriteDto { CatTitle = item.CatTitle, - CatString = item.CatString + CatString = item.CatString, + UpdateProcedure = 0 }; editingId = item.Guid; isEditing = true; diff --git a/DbFirst.Domain/CatalogUpdateProcedure.cs b/DbFirst.Domain/CatalogUpdateProcedure.cs new file mode 100644 index 0000000..fa02ee0 --- /dev/null +++ b/DbFirst.Domain/CatalogUpdateProcedure.cs @@ -0,0 +1,7 @@ +namespace DbFirst.Domain; + +public enum CatalogUpdateProcedure +{ + Update = 0, + Save = 1 +} diff --git a/DbFirst.Domain/Repositories/ICatalogRepository.cs b/DbFirst.Domain/Repositories/ICatalogRepository.cs index bd50be8..a9444d9 100644 --- a/DbFirst.Domain/Repositories/ICatalogRepository.cs +++ b/DbFirst.Domain/Repositories/ICatalogRepository.cs @@ -1,33 +1,14 @@ using DbFirst.Domain.Entities; +using DbFirst.Domain; namespace DbFirst.Domain.Repositories; -// TODO: instead of creating interface per entity, consider using generic repository pattern (eg. IRepository) to reduce code duplication. - -/* Copilot's Response: - -A generic Repository isn’t really worthwhile here: - • Reads from the view are generic, but inserts/updates/deletes go through stored procedures with special parameters/output GUIDs.You’d need lots of exceptions/overrides—little gain. - • Operations aren’t symmetric (separate procs for insert/update/delete with output handling and reload), so a one-size-fits-all CRUD pattern doesn’t fit well. - • Better to keep the specialized repo.If you want reuse, extract small helpers (e.g., for proc calls/output parameters/reload) instead of forcing a generic repository. */ - -// TODO: move to application layer as a part of clean architecture - -/* Copilot's Response: - - • Common: Keep the interface in the Domain layer, implementation in Infrastructure. Domain defines the data access contract, Infrastructure provides the implementation. - That matches typical DDD/Clean Architecture. - • Alternative: Move the interface to the Application layer. Then only Application depends on Infrastructure, Domain stays isolated from infra contracts. - Pro: Domain contains only models/rules. - Con: If Domain logic needs repos, it would depend on Application, weakening the layer separation. - Recommendation: Keep the interface in Domain. It’s conventional, clean, and avoids a Domain -> Application dependency. Implementation stays in Infrastructure. */ - public interface ICatalogRepository { Task> GetAllAsync(CancellationToken cancellationToken = default); Task GetByIdAsync(int id, CancellationToken cancellationToken = default); Task GetByTitleAsync(string title, CancellationToken cancellationToken = default); Task InsertAsync(VwmyCatalog catalog, CancellationToken cancellationToken = default); - Task UpdateAsync(int id, VwmyCatalog catalog, CancellationToken cancellationToken = default); + Task UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken cancellationToken = default); Task DeleteAsync(int id, CancellationToken cancellationToken = default); } diff --git a/DbFirst.Infrastructure/Repositories/CatalogRepository.cs b/DbFirst.Infrastructure/Repositories/CatalogRepository.cs index d80d5db..990b838 100644 --- a/DbFirst.Infrastructure/Repositories/CatalogRepository.cs +++ b/DbFirst.Infrastructure/Repositories/CatalogRepository.cs @@ -11,6 +11,7 @@ using System.Text; using System.Threading.Channels; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; using static System.Runtime.InteropServices.JavaScript.JSType; +using DbFirst.Domain; namespace DbFirst.Infrastructure.Repositories; @@ -78,7 +79,7 @@ public class CatalogRepository : ICatalogRepository return created; } - public async Task UpdateAsync(int id, VwmyCatalog catalog, CancellationToken cancellationToken = default) + public async Task UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken cancellationToken = default) { catalog.Guid = id; @@ -91,8 +92,12 @@ public class CatalogRepository : ICatalogRepository var catStringParam = new SqlParameter("@CAT_STRING", catalog.CatString); var changedWhoParam = new SqlParameter("@CHANGED_WHO", (object?)catalog.ChangedWho ?? DBNull.Value); + var procName = procedure == CatalogUpdateProcedure.Save + ? "PRTBMY_CATALOG_SAVE" + : "PRTBMY_CATALOG_UPDATE"; + await _db.Database.ExecuteSqlRawAsync( - "EXEC dbo.PRTBMY_CATALOG_UPDATE @CAT_TITLE, @CAT_STRING, @CHANGED_WHO, @GUID OUTPUT", + $"EXEC dbo.{procName} @CAT_TITLE, @CAT_STRING, @CHANGED_WHO, @GUID OUTPUT", parameters: new[] { catTitleParam, catStringParam, changedWhoParam, guidParam }, cancellationToken: cancellationToken);