Expanded using directives to support additional dependencies in CatalogRepository.cs. Added a detailed comment explaining why per-entity repository implementations are preferred over a generic CRUD base service in this context, due to unique domain logic and stored procedure requirements. No functional code changes were made.
137 lines
5.3 KiB
C#
137 lines
5.3 KiB
C#
using Azure;
|
||
using DbFirst.Domain.Entities;
|
||
using DbFirst.Domain.Repositories;
|
||
using Microsoft.Data.SqlClient;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Data;
|
||
using System.Text;
|
||
using System.Threading.Channels;
|
||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||
|
||
namespace DbFirst.Infrastructure.Repositories;
|
||
|
||
// TODO: instead of creating implementation of repository per entity, consider using generic repository pattern (eg. Repository<T>) 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. */
|
||
|
||
public class CatalogRepository : ICatalogRepository
|
||
{
|
||
private readonly ApplicationDbContext _db;
|
||
|
||
public CatalogRepository(ApplicationDbContext db)
|
||
{
|
||
_db = db;
|
||
}
|
||
|
||
public async Task<List<VwmyCatalog>> GetAllAsync(CancellationToken cancellationToken = default)
|
||
{
|
||
return await _db.VwmyCatalogs.AsNoTracking().ToListAsync(cancellationToken);
|
||
}
|
||
|
||
public async Task<VwmyCatalog?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
||
{
|
||
return await _db.VwmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == id, cancellationToken);
|
||
}
|
||
|
||
public async Task<VwmyCatalog?> GetByTitleAsync(string title, CancellationToken cancellationToken = default)
|
||
{
|
||
return await _db.VwmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.CatTitle == title, cancellationToken);
|
||
}
|
||
|
||
public async Task<VwmyCatalog> InsertAsync(VwmyCatalog catalog, CancellationToken cancellationToken = default)
|
||
{
|
||
var guidParam = new SqlParameter("@GUID", SqlDbType.Int)
|
||
{
|
||
Direction = ParameterDirection.Output
|
||
};
|
||
|
||
var catTitleParam = new SqlParameter("@CAT_TITLE", catalog.CatTitle);
|
||
var catStringParam = new SqlParameter("@CAT_STRING", catalog.CatString);
|
||
var addedWhoParam = new SqlParameter("@ADDED_WHO", (object?)catalog.AddedWho ?? DBNull.Value);
|
||
|
||
await _db.Database.ExecuteSqlRawAsync(
|
||
"EXEC dbo.PRTBMY_CATALOG_INSERT @CAT_TITLE, @CAT_STRING, @ADDED_WHO, @GUID OUTPUT",
|
||
parameters: new[] { catTitleParam, catStringParam, addedWhoParam, guidParam },
|
||
cancellationToken: cancellationToken);
|
||
|
||
if (guidParam.Value == DBNull.Value)
|
||
{
|
||
throw new InvalidOperationException("Failed to insert catalog via stored procedure.");
|
||
}
|
||
|
||
var guid = (int)guidParam.Value;
|
||
var created = await _db.VwmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == guid, cancellationToken);
|
||
if (created == null)
|
||
{
|
||
throw new InvalidOperationException("Inserted catalog could not be loaded from view.");
|
||
}
|
||
|
||
return created;
|
||
}
|
||
|
||
public async Task<VwmyCatalog?> UpdateAsync(int id, VwmyCatalog catalog, CancellationToken cancellationToken = default)
|
||
{
|
||
catalog.Guid = id;
|
||
|
||
var guidParam = new SqlParameter("@GUID", SqlDbType.Int)
|
||
{
|
||
Direction = ParameterDirection.Output
|
||
};
|
||
|
||
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;
|
||
if (guid == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return await _db.VwmyCatalogs.AsNoTracking().FirstOrDefaultAsync(x => x.Guid == guid, cancellationToken);
|
||
}
|
||
|
||
public async Task<bool> DeleteAsync(int id, CancellationToken cancellationToken = default)
|
||
{
|
||
var exists = await _db.VwmyCatalogs.AsNoTracking().AnyAsync(x => x.Guid == id, cancellationToken);
|
||
if (!exists)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var guidParam = new SqlParameter("@GUID", id);
|
||
await _db.Database.ExecuteSqlRawAsync(
|
||
"EXEC dbo.PRTBMY_CATALOG_DELETE @GUID",
|
||
parameters: new[] { guidParam },
|
||
cancellationToken: cancellationToken);
|
||
|
||
return true;
|
||
}
|
||
}
|