refactor: migrate to IDbContextFactory and Windows Service hosting
Replace AddDbContext with AddDbContextFactory for Blazor Server circuit safety — each service method now creates a short-lived DbContext via the factory. Configure Windows Service hosting with ContentRootPath and remove HSTS/HTTPS redirect for reverse-proxy deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,16 +6,18 @@ namespace CutList.Web.Services;
|
||||
|
||||
public class StockItemService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IDbContextFactory<ApplicationDbContext> _factory;
|
||||
|
||||
public StockItemService(ApplicationDbContext context)
|
||||
public StockItemService(IDbContextFactory<ApplicationDbContext> factory)
|
||||
{
|
||||
_context = context;
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public async Task<List<StockItem>> GetAllAsync(bool includeInactive = false)
|
||||
{
|
||||
var query = _context.StockItems
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var query = context.StockItems
|
||||
.Include(s => s.Material)
|
||||
.AsQueryable();
|
||||
|
||||
@@ -33,7 +35,9 @@ public class StockItemService
|
||||
|
||||
public async Task<List<StockItem>> GetByMaterialAsync(int materialId, bool includeInactive = false)
|
||||
{
|
||||
var query = _context.StockItems
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var query = context.StockItems
|
||||
.Include(s => s.Material)
|
||||
.Where(s => s.MaterialId == materialId);
|
||||
|
||||
@@ -49,7 +53,9 @@ public class StockItemService
|
||||
|
||||
public async Task<StockItem?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _context.StockItems
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
return await context.StockItems
|
||||
.Include(s => s.Material)
|
||||
.Include(s => s.SupplierOfferings)
|
||||
.ThenInclude(o => o.Supplier)
|
||||
@@ -58,33 +64,41 @@ public class StockItemService
|
||||
|
||||
public async Task<StockItem> CreateAsync(StockItem stockItem)
|
||||
{
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
stockItem.CreatedAt = DateTime.UtcNow;
|
||||
_context.StockItems.Add(stockItem);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockItems.Add(stockItem);
|
||||
await context.SaveChangesAsync();
|
||||
return stockItem;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(StockItem stockItem)
|
||||
{
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
_context.StockItems.Update(stockItem);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockItems.Update(stockItem);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(id);
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(id);
|
||||
if (stockItem != null)
|
||||
{
|
||||
stockItem.IsActive = false;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int materialId, decimal lengthInches, int? excludeId = null)
|
||||
{
|
||||
var query = _context.StockItems.Where(s =>
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var query = context.StockItems.Where(s =>
|
||||
s.MaterialId == materialId &&
|
||||
s.LengthInches == lengthInches &&
|
||||
s.IsActive);
|
||||
@@ -100,7 +114,9 @@ public class StockItemService
|
||||
// Stock transaction methods
|
||||
public async Task<StockTransaction> AddStockAsync(int stockItemId, int quantity, int? supplierId = null, decimal? unitPrice = null, string? notes = null)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||
|
||||
var transaction = new StockTransaction
|
||||
@@ -117,15 +133,17 @@ public class StockItemService
|
||||
stockItem.QuantityOnHand += quantity;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.StockTransactions.Add(transaction);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockTransactions.Add(transaction);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public async Task<decimal?> GetAverageCostAsync(int stockItemId)
|
||||
{
|
||||
var transactions = await _context.StockTransactions
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var transactions = await context.StockTransactions
|
||||
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
||||
.ToListAsync();
|
||||
|
||||
@@ -140,7 +158,9 @@ public class StockItemService
|
||||
|
||||
public async Task<decimal?> GetLastPurchasePriceAsync(int stockItemId)
|
||||
{
|
||||
return await _context.StockTransactions
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
return await context.StockTransactions
|
||||
.Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
|
||||
.OrderByDescending(t => t.CreatedAt)
|
||||
.Select(t => t.UnitPrice)
|
||||
@@ -149,7 +169,9 @@ public class StockItemService
|
||||
|
||||
public async Task<StockTransaction> UseStockAsync(int stockItemId, int quantity, int? jobId = null, string? notes = null)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||
|
||||
var transaction = new StockTransaction
|
||||
@@ -165,15 +187,17 @@ public class StockItemService
|
||||
stockItem.QuantityOnHand -= quantity;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.StockTransactions.Add(transaction);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockTransactions.Add(transaction);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public async Task<StockTransaction> AdjustStockAsync(int stockItemId, int newQuantity, string? notes = null)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||
|
||||
var difference = newQuantity - stockItem.QuantityOnHand;
|
||||
@@ -190,15 +214,17 @@ public class StockItemService
|
||||
stockItem.QuantityOnHand = newQuantity;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.StockTransactions.Add(transaction);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockTransactions.Add(transaction);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public async Task<StockTransaction> ScrapStockAsync(int stockItemId, int quantity, string? notes = null)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||
|
||||
var transaction = new StockTransaction
|
||||
@@ -213,15 +239,17 @@ public class StockItemService
|
||||
stockItem.QuantityOnHand -= quantity;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
_context.StockTransactions.Add(transaction);
|
||||
await _context.SaveChangesAsync();
|
||||
context.StockTransactions.Add(transaction);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return transaction;
|
||||
}
|
||||
|
||||
public async Task<List<StockTransaction>> GetTransactionHistoryAsync(int stockItemId, int? limit = null)
|
||||
{
|
||||
var query = _context.StockTransactions
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var query = context.StockTransactions
|
||||
.Include(t => t.Job)
|
||||
.Include(t => t.Supplier)
|
||||
.Where(t => t.StockItemId == stockItemId)
|
||||
@@ -238,10 +266,12 @@ public class StockItemService
|
||||
|
||||
public async Task<int> RecalculateQuantityAsync(int stockItemId)
|
||||
{
|
||||
var stockItem = await _context.StockItems.FindAsync(stockItemId)
|
||||
await using var context = _factory.CreateDbContext();
|
||||
|
||||
var stockItem = await context.StockItems.FindAsync(stockItemId)
|
||||
?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
|
||||
|
||||
var calculatedQuantity = await _context.StockTransactions
|
||||
var calculatedQuantity = await context.StockTransactions
|
||||
.Where(t => t.StockItemId == stockItemId)
|
||||
.SumAsync(t => t.Quantity);
|
||||
|
||||
@@ -249,7 +279,7 @@ public class StockItemService
|
||||
{
|
||||
stockItem.QuantityOnHand = calculatedQuantity;
|
||||
stockItem.UpdatedAt = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return calculatedQuantity;
|
||||
|
||||
Reference in New Issue
Block a user