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>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Enums;
|
||||
using PowderCoating.Core.Interfaces.Repositories;
|
||||
using PowderCoating.Infrastructure.Data;
|
||||
|
||||
@@ -60,4 +61,69 @@ public class NotificationLogRepository : Repository<NotificationLog>, INotificat
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user