Refactor catalog CRUD to use stored procedures only
Removed direct usage of TbmyCatalog entity and DbSet. All create, update, and delete operations now use stored procedures. Entity lookups and mappings are performed via the VwmyCatalog view. Updated AutoMapper profile and DbContext configuration accordingly. Catalog creation now sets ChangedWho/ChangedWhen to "system" and current UTC time.
This commit is contained in:
@@ -32,8 +32,8 @@ public class CatalogService : ICatalogService
|
|||||||
var domainItem = _mapper.Map<Catalog>(dto);
|
var domainItem = _mapper.Map<Catalog>(dto);
|
||||||
domainItem.AddedWho = "system";
|
domainItem.AddedWho = "system";
|
||||||
domainItem.AddedWhen = DateTime.UtcNow;
|
domainItem.AddedWhen = DateTime.UtcNow;
|
||||||
domainItem.ChangedWho = null;
|
domainItem.ChangedWho = "system";
|
||||||
domainItem.ChangedWhen = null;
|
domainItem.ChangedWhen = DateTime.UtcNow;
|
||||||
|
|
||||||
var created = await _repository.AddAsync(domainItem, cancellationToken);
|
var created = await _repository.AddAsync(domainItem, cancellationToken);
|
||||||
return _mapper.Map<CatalogReadDto>(created);
|
return _mapper.Map<CatalogReadDto>(created);
|
||||||
|
|||||||
@@ -10,49 +10,13 @@ public partial class ApplicationDbContext : DbContext
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual DbSet<TbmyCatalog> TbmyCatalogs { get; set; }
|
|
||||||
public virtual DbSet<VwmyCatalog> VwmyCatalogs { get; set; }
|
public virtual DbSet<VwmyCatalog> VwmyCatalogs { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
modelBuilder.Entity<TbmyCatalog>(entity =>
|
|
||||||
{
|
|
||||||
entity.HasKey(e => e.Guid);
|
|
||||||
|
|
||||||
entity.ToTable("TBMY_CATALOG");
|
|
||||||
|
|
||||||
entity.HasIndex(e => e.CatTitle, "UQ_TBMY_CATALOG_TITLE").IsUnique();
|
|
||||||
|
|
||||||
entity.Property(e => e.Guid).HasColumnName("GUID");
|
|
||||||
entity.Property(e => e.AddedWhen)
|
|
||||||
.HasDefaultValueSql("(getdate())")
|
|
||||||
.HasColumnType("datetime")
|
|
||||||
.HasColumnName("ADDED_WHEN");
|
|
||||||
entity.Property(e => e.AddedWho)
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.IsUnicode(false)
|
|
||||||
.HasDefaultValue("SYSTEM")
|
|
||||||
.HasColumnName("ADDED_WHO");
|
|
||||||
entity.Property(e => e.CatString)
|
|
||||||
.HasMaxLength(900)
|
|
||||||
.IsUnicode(false)
|
|
||||||
.HasColumnName("CAT_STRING");
|
|
||||||
entity.Property(e => e.CatTitle)
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.IsUnicode(false)
|
|
||||||
.HasColumnName("CAT_TITLE");
|
|
||||||
entity.Property(e => e.ChangedWhen)
|
|
||||||
.HasColumnType("datetime")
|
|
||||||
.HasColumnName("CHANGED_WHEN");
|
|
||||||
entity.Property(e => e.ChangedWho)
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.IsUnicode(false)
|
|
||||||
.HasColumnName("CHANGED_WHO");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity<VwmyCatalog>(entity =>
|
modelBuilder.Entity<VwmyCatalog>(entity =>
|
||||||
{
|
{
|
||||||
entity.HasNoKey();
|
entity.HasKey(e => e.Guid);
|
||||||
|
|
||||||
entity.ToView("VWMY_CATALOG");
|
entity.ToView("VWMY_CATALOG");
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public class CatalogInfrastructureProfile : Profile
|
|||||||
{
|
{
|
||||||
public CatalogInfrastructureProfile()
|
public CatalogInfrastructureProfile()
|
||||||
{
|
{
|
||||||
CreateMap<TbmyCatalog, Catalog>().ReverseMap();
|
|
||||||
CreateMap<VwmyCatalog, Catalog>();
|
CreateMap<VwmyCatalog, Catalog>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,24 +33,26 @@ public class CatalogRepository : ICatalogRepository
|
|||||||
|
|
||||||
public async Task<Catalog> AddAsync(Catalog catalog, CancellationToken cancellationToken = default)
|
public async Task<Catalog> AddAsync(Catalog catalog, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var entity = _mapper.Map<TbmyCatalog>(catalog);
|
var created = await UpsertWithStoredProcedureAsync(catalog, cancellationToken);
|
||||||
_db.TbmyCatalogs.Add(entity);
|
if (created == null)
|
||||||
await _db.SaveChangesAsync(cancellationToken);
|
{
|
||||||
return _mapper.Map<Catalog>(entity);
|
throw new InvalidOperationException("Failed to create catalog via stored procedure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdateAsync(int id, Catalog catalog, CancellationToken cancellationToken = default)
|
public async Task<bool> UpdateAsync(int id, Catalog catalog, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var entity = await _db.TbmyCatalogs.FirstOrDefaultAsync(x => x.Guid == id, cancellationToken);
|
var existing = await _db.VwmyCatalogs.AsNoTracking().AnyAsync(x => x.Guid == id, cancellationToken);
|
||||||
if (entity == null)
|
if (!existing)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mapper.Map(catalog, entity);
|
catalog.Guid = id;
|
||||||
entity.Guid = id;
|
var updated = await UpsertWithStoredProcedureAsync(catalog, cancellationToken);
|
||||||
await _db.SaveChangesAsync(cancellationToken);
|
return updated != null;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Catalog?> UpdateWithStoredProcedureAsync(Catalog catalog, CancellationToken cancellationToken = default)
|
public async Task<Catalog?> UpdateWithStoredProcedureAsync(Catalog catalog, CancellationToken cancellationToken = default)
|
||||||
@@ -60,48 +62,18 @@ public class CatalogRepository : ICatalogRepository
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var existing = await _db.TbmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == catalog.Guid, cancellationToken);
|
var existing = await _db.VwmyCatalogs.AsNoTracking().AnyAsync(x => x.Guid == catalog.Guid, cancellationToken);
|
||||||
if (existing == null)
|
if (!existing)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var catTitleParam = new SqlParameter("@CAT_TITLE", existing.CatTitle);
|
return await UpsertWithStoredProcedureAsync(catalog, cancellationToken);
|
||||||
var catStringParam = new SqlParameter("@CAT_STRING", catalog.CatString);
|
|
||||||
var changedWhoParam = new SqlParameter("@CHANGED_WHO", (object?)catalog.ChangedWho ?? DBNull.Value);
|
|
||||||
var guidOutParam = new SqlParameter("@GUID", SqlDbType.Int) { Direction = ParameterDirection.Output };
|
|
||||||
|
|
||||||
await _db.Database.ExecuteSqlRawAsync(
|
|
||||||
"EXEC dbo.PRTBMY_CATALOG_UPDATE @CAT_TITLE, @CAT_STRING, @CHANGED_WHO, @GUID OUTPUT",
|
|
||||||
parameters: new[] { catTitleParam, catStringParam, changedWhoParam, guidOutParam },
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
|
|
||||||
if (guidOutParam.Value == DBNull.Value)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var guid = (int)guidOutParam.Value;
|
|
||||||
var entity = await _db.TbmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == guid, cancellationToken);
|
|
||||||
return entity == null ? new Catalog { Guid = guid, CatTitle = existing.CatTitle, CatString = catalog.CatString, ChangedWho = catalog.ChangedWho } : _mapper.Map<Catalog>(entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> DeleteAsync(int id, CancellationToken cancellationToken = default)
|
public async Task<bool> DeleteAsync(int id, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var entity = await _db.TbmyCatalogs.FirstOrDefaultAsync(x => x.Guid == id, cancellationToken);
|
var exists = await _db.VwmyCatalogs.AsNoTracking().AnyAsync(x => x.Guid == id, cancellationToken);
|
||||||
if (entity == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_db.TbmyCatalogs.Remove(entity);
|
|
||||||
await _db.SaveChangesAsync(cancellationToken);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> DeleteWithStoredProcedureAsync(int id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var exists = await _db.TbmyCatalogs.AsNoTracking().AnyAsync(x => x.Guid == id, cancellationToken);
|
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -115,4 +87,36 @@ public class CatalogRepository : ICatalogRepository
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteWithStoredProcedureAsync(int id, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return await DeleteAsync(id, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Catalog?> UpsertWithStoredProcedureAsync(Catalog catalog, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var guidParam = new SqlParameter("@GUID", SqlDbType.Int)
|
||||||
|
{
|
||||||
|
Direction = ParameterDirection.InputOutput,
|
||||||
|
Value = catalog.Guid == 0 ? DBNull.Value : catalog.Guid
|
||||||
|
};
|
||||||
|
|
||||||
|
var catTitleParam = new SqlParameter("@CAT_TITLE", catalog.CatTitle);
|
||||||
|
var catStringParam = new SqlParameter("@CAT_STRING", catalog.CatString);
|
||||||
|
var changedWhoParam = new SqlParameter("@CHANGED_WHO", (object?)catalog.ChangedWho ?? DBNull.Value);
|
||||||
|
|
||||||
|
await _db.Database.ExecuteSqlRawAsync(
|
||||||
|
"EXEC dbo.PRTBMY_CATALOG_UPDATE @CAT_TITLE, @CAT_STRING, @CHANGED_WHO, @GUID OUTPUT",
|
||||||
|
parameters: new[] { catTitleParam, catStringParam, changedWhoParam, guidParam },
|
||||||
|
cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
if (guidParam.Value == DBNull.Value)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var guid = (int)guidParam.Value;
|
||||||
|
var viewRow = await _db.VwmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == guid, cancellationToken);
|
||||||
|
return viewRow == null ? null : _mapper.Map<Catalog>(viewRow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
namespace DbFirst.Infrastructure.ScaffoldEntities;
|
|
||||||
|
|
||||||
public partial class TbmyCatalog
|
|
||||||
{
|
|
||||||
public int Guid { get; set; }
|
|
||||||
|
|
||||||
public string CatTitle { get; set; } = null!;
|
|
||||||
|
|
||||||
public string CatString { get; set; } = null!;
|
|
||||||
|
|
||||||
public string AddedWho { get; set; } = null!;
|
|
||||||
|
|
||||||
public DateTime AddedWhen { get; set; }
|
|
||||||
|
|
||||||
public string? ChangedWho { get; set; }
|
|
||||||
|
|
||||||
public DateTime? ChangedWhen { get; set; }
|
|
||||||
|
|
||||||
// = null!; ist nur eine Null-Unterdrückung für Non‑Nullable-Referenztypen. Hintergrund: Die Spalten CatTitle, CatString, AddedWho
|
|
||||||
// sind in der DB als NOT NULL definiert, also generiert der Scaffold string (ohne ?). Damit der Compiler bei aktivierter Nullable-Analyse
|
|
||||||
// nicht warnt („non-nullable property is uninitialized“), wird ein Dummy-Init auf null! gesetzt. Das ! sagt dem Compiler „ich garantiere,
|
|
||||||
// dass zur Laufzeit ein Wert gesetzt wird“. Bei string? ChangedWho/DateTime? ChangedWhen sind die Spalten nullable, daher kein null! nötig.
|
|
||||||
// Bei Value Types wie int/DateTime braucht es ebenfalls kein Init, da sie Standardwerte haben.
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user