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);