Phase 2: Eliminate ApplicationDbContext from domain controllers

Migrated InvoicesController, QuotesController, JobsController, BillsController,
PurchaseOrdersController, and CustomersController to route all data access
through IUnitOfWork typed/generic repositories instead of injecting
ApplicationDbContext directly.

New typed repositories added: IJobRepository (GetScheduledJobsForDateAsync,
GetActiveJobsForMobileAsync, LoadForCostingAsync), INotificationLogRepository
(GetLatestForJobAsync, GetAllForJobAsync), IQuoteRepository (GetItemsWithCoatsAsync
with CatalogItem eager load + AsNoTracking), and IJobRepository.GetOrphanedConversionJobAsync.

All EF complex include chains relocated into repository methods; controllers now
call named query methods rather than composing raw IQueryable chains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 21:20:39 -04:00
parent 80b0e547cc
commit 90bc0d965f
20 changed files with 730 additions and 878 deletions
@@ -49,7 +49,9 @@ public class UnitOfWork : IUnitOfWork
private IRepository<JobDailyPriority>? _jobDailyPriorities;
private IRepository<JobItem>? _jobItems;
private IRepository<JobItemCoat>? _jobItemCoats;
private IRepository<JobItemPrepService>? _jobItemPrepServices;
private IRepository<JobChangeHistory>? _jobChangeHistories;
private IRepository<JobPrepService>? _jobPrepServices;
private IQuoteRepository? _quotes;
private IRepository<QuotePhoto>? _quotePhotos;
private IRepository<QuoteItem>? _quoteItems;
@@ -88,7 +90,7 @@ public class UnitOfWork : IUnitOfWork
private IRepository<CatalogPriceCheckReport>? _catalogPriceCheckReports;
// Notifications
private IRepository<NotificationLog>? _notificationLogs;
private INotificationLogRepository? _notificationLogs;
private IRepository<NotificationTemplate>? _notificationTemplates;
// Subscription
@@ -190,11 +192,17 @@ public class UnitOfWork : IUnitOfWork
/// <summary>Repository for <see cref="JobItemCoat"/> powder coat passes; tenant-filtered with soft delete.</summary>
public IRepository<JobItemCoat> JobItemCoats =>
_jobItemCoats ??= new Repository<JobItemCoat>(_context);
public IRepository<JobItemPrepService> JobItemPrepServices =>
_jobItemPrepServices ??= new Repository<JobItemPrepService>(_context);
/// <summary>Repository for <see cref="JobChangeHistory"/> audit entries; tenant-filtered with soft delete.</summary>
public IRepository<JobChangeHistory> JobChangeHistories =>
_jobChangeHistories ??= new Repository<JobChangeHistory>(_context);
/// <summary>Repository for <see cref="JobPrepService"/> job-level prep service assignments; tenant-filtered with soft delete.</summary>
public IRepository<JobPrepService> JobPrepServices =>
_jobPrepServices ??= new Repository<JobPrepService>(_context);
/// <summary>Repository for <see cref="Quote"/> records with multi-item pricing; tenant-filtered with soft delete.</summary>
public IQuoteRepository Quotes =>
_quotes ??= new QuoteRepository(_context);
@@ -351,9 +359,9 @@ public class UnitOfWork : IUnitOfWork
_catalogPriceCheckReports ??= new Repository<CatalogPriceCheckReport>(_context);
// Notifications
/// <summary>Repository for <see cref="NotificationLog"/> outbound notification audit records; tenant-filtered with soft delete.</summary>
public IRepository<NotificationLog> NotificationLogs =>
_notificationLogs ??= new Repository<NotificationLog>(_context);
/// <summary>Repository for <see cref="NotificationLog"/> outbound notification audit records; provides IgnoreQueryFilters lookups by InvoiceId, QuoteId, and JobId for notification history panels.</summary>
public INotificationLogRepository NotificationLogs =>
_notificationLogs ??= new NotificationLogRepository(_context);
/// <summary>Repository for <see cref="NotificationTemplate"/> per-company channel template overrides; unique on (CompanyId, Type, Channel).</summary>
public IRepository<NotificationTemplate> NotificationTemplates =>