1cb7a8ca4a
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>
130 lines
4.9 KiB
C#
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);
|
|
}
|
|
}
|