Files
PowderCoatingLogix/src/PowderCoating.Infrastructure/Repositories/NotificationLogRepository.cs
T
spouliot 1cb7a8ca4a Phases 3 & 4: Complete data access architecture migration
Phase 3 — eliminated ApplicationDbContext from all non-exempt controllers,
routing all data access through IUnitOfWork. Added IPlainRepository<T> for
the four platform entities (Announcement, BannedIp, DashboardTip, ReleaseNote)
that intentionally don't extend BaseEntity and therefore can't use the
constrained IRepository<T>. Added permanent-exception comments to the 18
controllers that legitimately retain direct DbContext access (Identity infra,
cross-tenant platform ops, bulk streaming exports).

Phase 4 — added EnforceDataAccessArchitecture() to Program.cs, a startup
gate that reflects over every Controller subclass and throws at boot if any
non-exempt controller injects ApplicationDbContext. The app cannot start with
a violation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 09:17:29 -04:00

130 lines
4.9 KiB
C#

using Microsoft.EntityFrameworkCore;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Core.Interfaces.Repositories;
using PowderCoating.Infrastructure.Data;
namespace PowderCoating.Infrastructure.Repositories;
/// <summary>
/// Typed repository for <see cref="NotificationLog"/> that provides IgnoreQueryFilters-based
/// lookups by entity FK (InvoiceId, QuoteId, JobId).
/// </summary>
public class NotificationLogRepository : Repository<NotificationLog>, INotificationLogRepository
{
public NotificationLogRepository(ApplicationDbContext context) : base(context) { }
/// <inheritdoc/>
public async Task<NotificationLog?> GetLatestForInvoiceAsync(int invoiceId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.InvoiceId == invoiceId)
.OrderByDescending(n => n.SentAt)
.FirstOrDefaultAsync();
/// <inheritdoc/>
public async Task<List<NotificationLog>> GetAllForInvoiceAsync(int invoiceId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.InvoiceId == invoiceId)
.OrderByDescending(n => n.SentAt)
.ToListAsync();
/// <inheritdoc/>
public async Task<NotificationLog?> GetLatestForQuoteAsync(int quoteId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.QuoteId == quoteId)
.OrderByDescending(n => n.SentAt)
.FirstOrDefaultAsync();
/// <inheritdoc/>
public async Task<List<NotificationLog>> GetAllForQuoteAsync(int quoteId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.QuoteId == quoteId)
.OrderByDescending(n => n.SentAt)
.ToListAsync();
/// <inheritdoc/>
public async Task<NotificationLog?> GetLatestForJobAsync(int jobId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.JobId == jobId)
.OrderByDescending(n => n.SentAt)
.FirstOrDefaultAsync();
/// <inheritdoc/>
public async Task<List<NotificationLog>> GetAllForJobAsync(int jobId) =>
await _context.Set<NotificationLog>()
.IgnoreQueryFilters()
.Where(n => n.JobId == jobId)
.OrderByDescending(n => n.SentAt)
.ToListAsync();
/// <inheritdoc/>
public async Task<(List<NotificationLog> Items, int TotalCount)> GetPagedFilteredAsync(
int pageNumber, int pageSize,
string? searchTerm = null,
NotificationChannel? channel = null,
NotificationStatus? status = null,
NotificationType? type = null,
int? jobId = null,
string sortColumn = "SentAt",
string sortDirection = "desc")
{
var query = _dbSet
.AsNoTracking()
.Include(n => n.Customer)
.Include(n => n.Job)
.Include(n => n.Quote)
.AsQueryable();
if (jobId.HasValue)
query = query.Where(n => n.JobId == jobId.Value);
if (!string.IsNullOrWhiteSpace(searchTerm))
{
var s = searchTerm.ToLower();
query = query.Where(n =>
n.RecipientName.ToLower().Contains(s) ||
n.Recipient.ToLower().Contains(s) ||
(n.Subject != null && n.Subject.ToLower().Contains(s)) ||
(n.Job != null && n.Job.JobNumber.ToLower().Contains(s)) ||
(n.Quote != null && n.Quote.QuoteNumber.ToLower().Contains(s)));
}
if (channel.HasValue)
query = query.Where(n => n.Channel == channel.Value);
if (status.HasValue)
query = query.Where(n => n.Status == status.Value);
if (type.HasValue)
query = query.Where(n => n.NotificationType == type.Value);
var totalCount = await query.CountAsync();
query = (sortColumn, sortDirection) switch
{
("RecipientName", "asc") => query.OrderBy(n => n.RecipientName),
("RecipientName", _) => query.OrderByDescending(n => n.RecipientName),
("Channel", "asc") => query.OrderBy(n => n.Channel),
("Channel", _) => query.OrderByDescending(n => n.Channel),
("Status", "asc") => query.OrderBy(n => n.Status),
("Status", _) => query.OrderByDescending(n => n.Status),
("Type", "asc") => query.OrderBy(n => n.NotificationType),
("Type", _) => query.OrderByDescending(n => n.NotificationType),
(_, "asc") => query.OrderBy(n => n.SentAt),
_ => query.OrderByDescending(n => n.SentAt)
};
var items = await query
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return (items, totalCount);
}
}