Compare commits

...

4 Commits

Author SHA1 Message Date
OlgunR
8d3783cfec Update CORS config; add architecture discussion comments
- Make CORS policy environment-aware: allow any origin in development, restrict to configured origins in production.
- Add detailed comments in CatalogService.cs and ICatalogRepository.cs discussing generic CRUD services, CQRS with MediatR, and repository interface placement, including both Copilot's and Hakan's perspectives.
- No functional changes to service or repository logic.
2026-01-19 14:48:55 +01:00
OlgunR
0af0c4589d Restrict CatTitle editing based on UpdateProcedure
Enforce business rules for catalog title changes: only allow CatTitle to be edited when UpdateProcedure permits, with checks in the API, service, handler, and UI. This ensures consistent validation and user experience across backend and frontend.
2026-01-19 14:44:55 +01:00
OlgunR
26f783e835 Prevent catalog title changes during edit
Enforce immutability of CatTitle on updates: backend now rejects title changes with a BadRequest, and frontend disables the title input field when editing.
2026-01-19 11:21:33 +01:00
OlgunR
4fbcd0dc11 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.
2026-01-19 11:10:19 +01:00
9 changed files with 50 additions and 12 deletions

View File

@@ -1,6 +1,7 @@
using DbFirst.Application.Catalogs;
using DbFirst.Application.Catalogs.Commands;
using DbFirst.Application.Catalogs.Queries;
using DbFirst.Domain;
using MediatR;
using Microsoft.AspNetCore.Mvc;
@@ -54,9 +55,10 @@ public class CatalogsController : ControllerBase
{
return NotFound();
}
if (!string.Equals(current.CatTitle, dto.CatTitle, StringComparison.Ordinal))
if (dto.UpdateProcedure == CatalogUpdateProcedure.Update &&
!string.Equals(current.CatTitle, dto.CatTitle, StringComparison.OrdinalIgnoreCase))
{
return BadRequest("CatTitle cannot be changed.");
return BadRequest("Titel kann nicht geändert werden.");
}
var updated = await _mediator.Send(new UpdateCatalogCommand(id, dto), cancellationToken);

View File

@@ -1,6 +1,7 @@
using AutoMapper;
using DbFirst.Domain.Repositories;
using DbFirst.Domain.Entities;
using DbFirst.Domain;
namespace DbFirst.Application.Catalogs;
@@ -83,13 +84,14 @@ public class CatalogService : ICatalogService
var entity = _mapper.Map<VwmyCatalog>(dto);
entity.Guid = id;
entity.CatTitle = existing.CatTitle;
entity.CatTitle = dto.UpdateProcedure == CatalogUpdateProcedure.Update ? existing.CatTitle : dto.CatTitle;
entity.AddedWho = existing.AddedWho;
entity.AddedWhen = existing.AddedWhen;
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<CatalogReadDto>(updated);
}

View File

@@ -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;
}

View File

@@ -1,6 +1,7 @@
using AutoMapper;
using DbFirst.Domain.Entities;
using DbFirst.Domain.Repositories;
using DbFirst.Domain;
using MediatR;
namespace DbFirst.Application.Catalogs.Commands;
@@ -26,13 +27,16 @@ public class UpdateCatalogHandler : IRequestHandler<UpdateCatalogCommand, Catalo
var entity = _mapper.Map<VwmyCatalog>(request.Dto);
entity.Guid = request.Id;
entity.CatTitle = existing.CatTitle;
entity.CatTitle = request.Dto.UpdateProcedure == CatalogUpdateProcedure.Update
? existing.CatTitle
: request.Dto.CatTitle;
entity.AddedWho = existing.AddedWho;
entity.AddedWhen = existing.AddedWhen;
entity.ChangedWho = "system";
entity.ChangedWhen = DateTime.UtcNow;
var updated = await _repository.UpdateAsync(request.Id, entity, cancellationToken);
var procedure = request.Dto.UpdateProcedure;
var updated = await _repository.UpdateAsync(request.Id, entity, procedure, cancellationToken);
return updated == null ? null : _mapper.Map<CatalogReadDto>(updated);
}
}

View File

@@ -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
}

View File

@@ -24,7 +24,7 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Titel</label>
<InputText class="form-control" @bind-Value="formModel.CatTitle" />
<InputText class="form-control" @bind-Value="formModel.CatTitle" disabled="@(isEditing && formModel.UpdateProcedure == 0)" />
</div>
<div class="col-md-6">
<label class="form-label">Kennung</label>
@@ -32,6 +32,19 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
</div>
</div>
@if (isEditing)
{
<div class="row g-3 mt-2">
<div class="col-md-6">
<label class="form-label">Update-Prozedur</label>
<InputSelect class="form-control" @bind-Value="formModel.UpdateProcedure">
<option value="0">PRTBMY_CATALOG_UPDATE</option>
<option value="1">PRTBMY_CATALOG_SAVE</option>
</InputSelect>
</div>
</div>
}
<div class="mt-3 d-flex gap-2">
<button type="submit" class="btn btn-success">@((isEditing ? "Speichern" : "Anlegen"))</button>
<button type="button" class="btn btn-secondary" @onclick="CancelEdit">Abbrechen</button>
@@ -132,7 +145,8 @@ else
formModel = new CatalogWriteDto
{
CatTitle = item.CatTitle,
CatString = item.CatString
CatString = item.CatString,
UpdateProcedure = 0
};
editingId = item.Guid;
isEditing = true;

View File

@@ -0,0 +1,7 @@
namespace DbFirst.Domain;
public enum CatalogUpdateProcedure
{
Update = 0,
Save = 1
}

View File

@@ -1,5 +1,5 @@
using DbFirst.Domain.Entities;
using System.Runtime.Intrinsics.X86;
using DbFirst.Domain;
namespace DbFirst.Domain.Repositories;
@@ -41,6 +41,6 @@ public interface ICatalogRepository
Task<VwmyCatalog?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
Task<VwmyCatalog?> GetByTitleAsync(string title, CancellationToken cancellationToken = default);
Task<VwmyCatalog> InsertAsync(VwmyCatalog catalog, CancellationToken cancellationToken = default);
Task<VwmyCatalog?> UpdateAsync(int id, VwmyCatalog catalog, CancellationToken cancellationToken = default);
Task<VwmyCatalog?> UpdateAsync(int id, VwmyCatalog catalog, CatalogUpdateProcedure procedure, CancellationToken cancellationToken = default);
Task<bool> DeleteAsync(int id, CancellationToken cancellationToken = default);
}

View File

@@ -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<VwmyCatalog?> UpdateAsync(int id, VwmyCatalog catalog, CancellationToken cancellationToken = default)
public async Task<VwmyCatalog?> 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);