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:
aj
2026-03-12 13:48:30 -04:00
co-authored by Claude Opus 4.6
parent f66fd3eaa7
commit b5bef23c1d
8 changed files with 352 additions and 226 deletions
+9 -5
View File
@@ -3,7 +3,13 @@ using CutList.Web.Data;
using CutList.Web.Services; using CutList.Web.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ContentRootPath = AppContext.BaseDirectory
});
builder.Host.UseWindowsService();
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(); builder.Services.AddControllers();
@@ -13,8 +19,8 @@ builder.Services.AddRazorComponents()
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
// Add Entity Framework // Add Entity Framework (factory pattern for Blazor Server circuit safety)
builder.Services.AddDbContext<ApplicationDbContext>(options => builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Add application services // Add application services
@@ -38,10 +44,8 @@ if (app.Environment.IsDevelopment())
else else
{ {
app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
} }
app.UseHttpsRedirection();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseAntiforgery(); app.UseAntiforgery();
+43 -40
View File
@@ -7,30 +7,32 @@ namespace CutList.Web.Services;
public class CatalogService public class CatalogService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
private readonly MaterialService _materialService; private readonly MaterialService _materialService;
public CatalogService(ApplicationDbContext context, MaterialService materialService) public CatalogService(IDbContextFactory<ApplicationDbContext> factory, MaterialService materialService)
{ {
_context = context; _factory = factory;
_materialService = materialService; _materialService = materialService;
} }
public async Task<CatalogData> ExportAsync() public async Task<CatalogData> ExportAsync()
{ {
var suppliers = await _context.Suppliers await using var context = _factory.CreateDbContext();
var suppliers = await context.Suppliers
.Where(s => s.IsActive) .Where(s => s.IsActive)
.OrderBy(s => s.Name) .OrderBy(s => s.Name)
.AsNoTracking() .AsNoTracking()
.ToListAsync(); .ToListAsync();
var cuttingTools = await _context.CuttingTools var cuttingTools = await context.CuttingTools
.Where(t => t.IsActive) .Where(t => t.IsActive)
.OrderBy(t => t.Name) .OrderBy(t => t.Name)
.AsNoTracking() .AsNoTracking()
.ToListAsync(); .ToListAsync();
var materials = await _context.Materials var materials = await context.Materials
.Include(m => m.Dimensions) .Include(m => m.Dimensions)
.Include(m => m.StockItems.Where(s => s.IsActive)) .Include(m => m.StockItems.Where(s => s.IsActive))
.ThenInclude(s => s.SupplierOfferings.Where(o => o.IsActive)) .ThenInclude(s => s.SupplierOfferings.Where(o => o.IsActive))
@@ -157,18 +159,19 @@ public class CatalogService
{ {
var result = new ImportResultDto(); var result = new ImportResultDto();
await using var transaction = await _context.Database.BeginTransactionAsync(); await using var context = _factory.CreateDbContext();
await using var transaction = await context.Database.BeginTransactionAsync();
try try
{ {
// 1. Suppliers - upsert by name // 1. Suppliers - upsert by name
var supplierMap = await ImportSuppliersAsync(data.Suppliers, result); var supplierMap = await ImportSuppliersAsync(context, data.Suppliers, result);
// 2. Cutting tools - upsert by name // 2. Cutting tools - upsert by name
await ImportCuttingToolsAsync(data.CuttingTools, result); await ImportCuttingToolsAsync(context, data.CuttingTools, result);
// 3. Materials + stock items + offerings // 3. Materials + stock items + offerings
await ImportAllMaterialsAsync(data.Materials, supplierMap, result); await ImportAllMaterialsAsync(context, data.Materials, supplierMap, result);
await transaction.CommitAsync(); await transaction.CommitAsync();
} }
@@ -182,11 +185,11 @@ public class CatalogService
} }
private async Task<Dictionary<string, int>> ImportSuppliersAsync( private async Task<Dictionary<string, int>> ImportSuppliersAsync(
List<CatalogSupplierDto> suppliers, ImportResultDto result) ApplicationDbContext context, List<CatalogSupplierDto> suppliers, ImportResultDto result)
{ {
var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); var map = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
var existingSuppliers = await _context.Suppliers.ToListAsync(); var existingSuppliers = await context.Suppliers.ToListAsync();
foreach (var dto in suppliers) foreach (var dto in suppliers)
{ {
@@ -212,8 +215,8 @@ public class CatalogService
Notes = dto.Notes, Notes = dto.Notes,
CreatedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
_context.Suppliers.Add(supplier); context.Suppliers.Add(supplier);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
existingSuppliers.Add(supplier); existingSuppliers.Add(supplier);
map[dto.Name] = supplier.Id; map[dto.Name] = supplier.Id;
result.SuppliersCreated++; result.SuppliersCreated++;
@@ -225,14 +228,14 @@ public class CatalogService
} }
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return map; return map;
} }
private async Task ImportCuttingToolsAsync( private async Task ImportCuttingToolsAsync(
List<CatalogCuttingToolDto> tools, ImportResultDto result) ApplicationDbContext context, List<CatalogCuttingToolDto> tools, ImportResultDto result)
{ {
var existingTools = await _context.CuttingTools.ToListAsync(); var existingTools = await context.CuttingTools.ToListAsync();
foreach (var dto in tools) foreach (var dto in tools)
{ {
@@ -255,7 +258,7 @@ public class CatalogService
KerfInches = dto.KerfInches, KerfInches = dto.KerfInches,
IsDefault = false IsDefault = false
}; };
_context.CuttingTools.Add(tool); context.CuttingTools.Add(tool);
existingTools.Add(tool); existingTools.Add(tool);
result.CuttingToolsCreated++; result.CuttingToolsCreated++;
} }
@@ -266,71 +269,71 @@ public class CatalogService
} }
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
private async Task ImportAllMaterialsAsync( private async Task ImportAllMaterialsAsync(
CatalogMaterialsDto materials, Dictionary<string, int> supplierMap, ImportResultDto result) ApplicationDbContext context, CatalogMaterialsDto materials, Dictionary<string, int> supplierMap, ImportResultDto result)
{ {
var existingMaterials = await _context.Materials var existingMaterials = await context.Materials
.Include(m => m.Dimensions) .Include(m => m.Dimensions)
.Include(m => m.StockItems) .Include(m => m.StockItems)
.ThenInclude(s => s.SupplierOfferings) .ThenInclude(s => s.SupplierOfferings)
.ToListAsync(); .ToListAsync();
foreach (var dto in materials.Angles) foreach (var dto in materials.Angles)
await ImportMaterialAsync(dto, MaterialShape.Angle, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.Angle, existingMaterials, supplierMap, result,
() => new AngleDimensions { Leg1 = dto.Leg1, Leg2 = dto.Leg2, Thickness = dto.Thickness }, () => new AngleDimensions { Leg1 = dto.Leg1, Leg2 = dto.Leg2, Thickness = dto.Thickness },
dim => { var d = (AngleDimensions)dim; d.Leg1 = dto.Leg1; d.Leg2 = dto.Leg2; d.Thickness = dto.Thickness; }); dim => { var d = (AngleDimensions)dim; d.Leg1 = dto.Leg1; d.Leg2 = dto.Leg2; d.Thickness = dto.Thickness; });
foreach (var dto in materials.Channels) foreach (var dto in materials.Channels)
await ImportMaterialAsync(dto, MaterialShape.Channel, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.Channel, existingMaterials, supplierMap, result,
() => new ChannelDimensions { Height = dto.Height, Flange = dto.Flange, Web = dto.Web }, () => new ChannelDimensions { Height = dto.Height, Flange = dto.Flange, Web = dto.Web },
dim => { var d = (ChannelDimensions)dim; d.Height = dto.Height; d.Flange = dto.Flange; d.Web = dto.Web; }); dim => { var d = (ChannelDimensions)dim; d.Height = dto.Height; d.Flange = dto.Flange; d.Web = dto.Web; });
foreach (var dto in materials.FlatBars) foreach (var dto in materials.FlatBars)
await ImportMaterialAsync(dto, MaterialShape.FlatBar, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.FlatBar, existingMaterials, supplierMap, result,
() => new FlatBarDimensions { Width = dto.Width, Thickness = dto.Thickness }, () => new FlatBarDimensions { Width = dto.Width, Thickness = dto.Thickness },
dim => { var d = (FlatBarDimensions)dim; d.Width = dto.Width; d.Thickness = dto.Thickness; }); dim => { var d = (FlatBarDimensions)dim; d.Width = dto.Width; d.Thickness = dto.Thickness; });
foreach (var dto in materials.IBeams) foreach (var dto in materials.IBeams)
await ImportMaterialAsync(dto, MaterialShape.IBeam, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.IBeam, existingMaterials, supplierMap, result,
() => new IBeamDimensions { Height = dto.Height, WeightPerFoot = dto.WeightPerFoot }, () => new IBeamDimensions { Height = dto.Height, WeightPerFoot = dto.WeightPerFoot },
dim => { var d = (IBeamDimensions)dim; d.Height = dto.Height; d.WeightPerFoot = dto.WeightPerFoot; }); dim => { var d = (IBeamDimensions)dim; d.Height = dto.Height; d.WeightPerFoot = dto.WeightPerFoot; });
foreach (var dto in materials.Pipes) foreach (var dto in materials.Pipes)
await ImportMaterialAsync(dto, MaterialShape.Pipe, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.Pipe, existingMaterials, supplierMap, result,
() => new PipeDimensions { NominalSize = dto.NominalSize, Wall = dto.Wall, Schedule = dto.Schedule }, () => new PipeDimensions { NominalSize = dto.NominalSize, Wall = dto.Wall, Schedule = dto.Schedule },
dim => { var d = (PipeDimensions)dim; d.NominalSize = dto.NominalSize; d.Wall = (decimal?)dto.Wall; d.Schedule = dto.Schedule; }); dim => { var d = (PipeDimensions)dim; d.NominalSize = dto.NominalSize; d.Wall = (decimal?)dto.Wall; d.Schedule = dto.Schedule; });
foreach (var dto in materials.RectangularTubes) foreach (var dto in materials.RectangularTubes)
await ImportMaterialAsync(dto, MaterialShape.RectangularTube, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.RectangularTube, existingMaterials, supplierMap, result,
() => new RectangularTubeDimensions { Width = dto.Width, Height = dto.Height, Wall = dto.Wall }, () => new RectangularTubeDimensions { Width = dto.Width, Height = dto.Height, Wall = dto.Wall },
dim => { var d = (RectangularTubeDimensions)dim; d.Width = dto.Width; d.Height = dto.Height; d.Wall = dto.Wall; }); dim => { var d = (RectangularTubeDimensions)dim; d.Width = dto.Width; d.Height = dto.Height; d.Wall = dto.Wall; });
foreach (var dto in materials.RoundBars) foreach (var dto in materials.RoundBars)
await ImportMaterialAsync(dto, MaterialShape.RoundBar, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.RoundBar, existingMaterials, supplierMap, result,
() => new RoundBarDimensions { Diameter = dto.Diameter }, () => new RoundBarDimensions { Diameter = dto.Diameter },
dim => { var d = (RoundBarDimensions)dim; d.Diameter = dto.Diameter; }); dim => { var d = (RoundBarDimensions)dim; d.Diameter = dto.Diameter; });
foreach (var dto in materials.RoundTubes) foreach (var dto in materials.RoundTubes)
await ImportMaterialAsync(dto, MaterialShape.RoundTube, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.RoundTube, existingMaterials, supplierMap, result,
() => new RoundTubeDimensions { OuterDiameter = dto.OuterDiameter, Wall = dto.Wall }, () => new RoundTubeDimensions { OuterDiameter = dto.OuterDiameter, Wall = dto.Wall },
dim => { var d = (RoundTubeDimensions)dim; d.OuterDiameter = dto.OuterDiameter; d.Wall = dto.Wall; }); dim => { var d = (RoundTubeDimensions)dim; d.OuterDiameter = dto.OuterDiameter; d.Wall = dto.Wall; });
foreach (var dto in materials.SquareBars) foreach (var dto in materials.SquareBars)
await ImportMaterialAsync(dto, MaterialShape.SquareBar, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.SquareBar, existingMaterials, supplierMap, result,
() => new SquareBarDimensions { Size = dto.SideLength }, () => new SquareBarDimensions { Size = dto.SideLength },
dim => { var d = (SquareBarDimensions)dim; d.Size = dto.SideLength; }); dim => { var d = (SquareBarDimensions)dim; d.Size = dto.SideLength; });
foreach (var dto in materials.SquareTubes) foreach (var dto in materials.SquareTubes)
await ImportMaterialAsync(dto, MaterialShape.SquareTube, existingMaterials, supplierMap, result, await ImportMaterialAsync(context, dto, MaterialShape.SquareTube, existingMaterials, supplierMap, result,
() => new SquareTubeDimensions { Size = dto.SideLength, Wall = dto.Wall }, () => new SquareTubeDimensions { Size = dto.SideLength, Wall = dto.Wall },
dim => { var d = (SquareTubeDimensions)dim; d.Size = dto.SideLength; d.Wall = dto.Wall; }); dim => { var d = (SquareTubeDimensions)dim; d.Size = dto.SideLength; d.Wall = dto.Wall; });
} }
private async Task ImportMaterialAsync( private async Task ImportMaterialAsync(
CatalogMaterialBaseDto dto, MaterialShape shape, ApplicationDbContext context, CatalogMaterialBaseDto dto, MaterialShape shape,
List<Material> existingMaterials, Dictionary<string, int> supplierMap, List<Material> existingMaterials, Dictionary<string, int> supplierMap,
ImportResultDto result, ImportResultDto result,
Func<MaterialDimensions> createDimensions, Func<MaterialDimensions> createDimensions,
@@ -384,9 +387,9 @@ public class CatalogService
result.MaterialsCreated++; result.MaterialsCreated++;
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
await ImportStockItemsAsync(material, dto.StockItems, supplierMap, result); await ImportStockItemsAsync(context, material, dto.StockItems, supplierMap, result);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -395,10 +398,10 @@ public class CatalogService
} }
private async Task ImportStockItemsAsync( private async Task ImportStockItemsAsync(
Material material, List<CatalogStockItemDto> stockItems, ApplicationDbContext context, Material material, List<CatalogStockItemDto> stockItems,
Dictionary<string, int> supplierMap, ImportResultDto result) Dictionary<string, int> supplierMap, ImportResultDto result)
{ {
var existingStockItems = await _context.StockItems var existingStockItems = await context.StockItems
.Include(s => s.SupplierOfferings) .Include(s => s.SupplierOfferings)
.Where(s => s.MaterialId == material.Id) .Where(s => s.MaterialId == material.Id)
.ToListAsync(); .ToListAsync();
@@ -432,8 +435,8 @@ public class CatalogService
Notes = dto.Notes, Notes = dto.Notes,
CreatedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
_context.StockItems.Add(stockItem); context.StockItems.Add(stockItem);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
existingStockItems.Add(stockItem); existingStockItems.Add(stockItem);
result.StockItemsCreated++; result.StockItemsCreated++;
} }
@@ -473,7 +476,7 @@ public class CatalogService
Price = offeringDto.Price, Price = offeringDto.Price,
Notes = offeringDto.Notes Notes = offeringDto.Notes
}; };
_context.SupplierOfferings.Add(offering); context.SupplierOfferings.Add(offering);
stockItem.SupplierOfferings.Add(offering); stockItem.SupplierOfferings.Add(offering);
result.OfferingsCreated++; result.OfferingsCreated++;
} }
@@ -486,7 +489,7 @@ public class CatalogService
} }
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
+12 -9
View File
@@ -8,11 +8,11 @@ namespace CutList.Web.Services;
public class CutListPackingService public class CutListPackingService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
public CutListPackingService(ApplicationDbContext context) public CutListPackingService(IDbContextFactory<ApplicationDbContext> factory)
{ {
_context = context; _factory = factory;
} }
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches) public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches)
@@ -22,6 +22,7 @@ public class CutListPackingService
public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches, IEnumerable<JobStock>? jobStock) public async Task<MultiMaterialPackResult> PackAsync(IEnumerable<JobPart> parts, decimal kerfInches, IEnumerable<JobStock>? jobStock)
{ {
await using var context = _factory.CreateDbContext();
var result = new MultiMaterialPackResult(); var result = new MultiMaterialPackResult();
// Group parts by material // Group parts by material
@@ -37,7 +38,7 @@ public class CutListPackingService
var materialParts = group.ToList(); var materialParts = group.ToList();
// Get the material // Get the material
var material = await _context.Materials var material = await context.Materials
.FirstOrDefaultAsync(m => m.Id == materialId); .FirstOrDefaultAsync(m => m.Id == materialId);
if (material == null) continue; if (material == null) continue;
@@ -63,7 +64,7 @@ public class CutListPackingService
else else
{ {
// No job-specific stock - use all available stock items for this material // No job-specific stock - use all available stock items for this material
var stockItems = await _context.StockItems var stockItems = await context.StockItems
.Where(s => s.MaterialId == materialId && s.IsActive) .Where(s => s.MaterialId == materialId && s.IsActive)
.ToListAsync(); .ToListAsync();
@@ -172,10 +173,12 @@ public class CutListPackingService
return result; return result;
} }
public MultiMaterialPackResult? LoadSavedResult(string json) public async Task<MultiMaterialPackResult?> LoadSavedResultAsync(string json)
{ {
var saved = System.Text.Json.JsonSerializer.Deserialize<SavedOptimizationResult>(json); var saved = System.Text.Json.JsonSerializer.Deserialize<SavedOptimizationResult>(json);
return saved?.ToPackResult(_context); if (saved == null) return null;
await using var context = _factory.CreateDbContext();
return await saved.ToPackResultAsync(context);
} }
public string SerializeResult(MultiMaterialPackResult result) public string SerializeResult(MultiMaterialPackResult result)
@@ -344,13 +347,13 @@ public class SavedOptimizationResult
return saved; return saved;
} }
public MultiMaterialPackResult ToPackResult(ApplicationDbContext context) public async Task<MultiMaterialPackResult> ToPackResultAsync(ApplicationDbContext context)
{ {
var result = new MultiMaterialPackResult(); var result = new MultiMaterialPackResult();
foreach (var savedMr in MaterialResults) foreach (var savedMr in MaterialResults)
{ {
var material = context.Materials.Find(savedMr.MaterialId); var material = await context.Materials.FindAsync(savedMr.MaterialId);
if (material == null) continue; if (material == null) continue;
var packResult = new PackResult(); var packResult = new PackResult();
+108 -69
View File
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
public class JobService public class JobService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
public JobService(ApplicationDbContext context) public JobService(IDbContextFactory<ApplicationDbContext> factory)
{ {
_context = context; _factory = factory;
} }
public async Task<List<Job>> GetAllAsync() public async Task<List<Job>> GetAllAsync()
{ {
return await _context.Jobs await using var context = _factory.CreateDbContext();
return await context.Jobs
.Include(p => p.CuttingTool) .Include(p => p.CuttingTool)
.Include(p => p.Parts) .Include(p => p.Parts)
.ThenInclude(pt => pt.Material) .ThenInclude(pt => pt.Material)
@@ -25,7 +26,8 @@ public class JobService
public async Task<Job?> GetByIdAsync(int id) public async Task<Job?> GetByIdAsync(int id)
{ {
return await _context.Jobs await using var context = _factory.CreateDbContext();
return await context.Jobs
.Include(p => p.CuttingTool) .Include(p => p.CuttingTool)
.Include(p => p.Parts.OrderBy(pt => pt.SortOrder)) .Include(p => p.Parts.OrderBy(pt => pt.SortOrder))
.ThenInclude(pt => pt.Material) .ThenInclude(pt => pt.Material)
@@ -38,17 +40,24 @@ public class JobService
public async Task<Job> CreateAsync(Job? job = null) public async Task<Job> CreateAsync(Job? job = null)
{ {
await using var context = _factory.CreateDbContext();
job ??= new Job(); job ??= new Job();
job.JobNumber = await GenerateJobNumberAsync(); job.JobNumber = await GenerateJobNumberAsync(context);
job.CreatedAt = DateTime.UtcNow; job.CreatedAt = DateTime.UtcNow;
_context.Jobs.Add(job); context.Jobs.Add(job);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return job; return job;
} }
public async Task<string> GenerateJobNumberAsync() public async Task<string> GenerateJobNumberAsync()
{ {
var maxNumber = await _context.Jobs await using var context = _factory.CreateDbContext();
return await GenerateJobNumberAsync(context);
}
private static async Task<string> GenerateJobNumberAsync(ApplicationDbContext context)
{
var maxNumber = await context.Jobs
.Where(j => j.JobNumber.StartsWith("JOB-")) .Where(j => j.JobNumber.StartsWith("JOB-"))
.Select(j => j.JobNumber) .Select(j => j.JobNumber)
.MaxAsync() as string; .MaxAsync() as string;
@@ -71,46 +80,61 @@ public class JobService
public async Task UpdateAsync(Job job) public async Task UpdateAsync(Job job)
{ {
await using var context = _factory.CreateDbContext();
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
_context.Jobs.Update(job); context.Jobs.Update(job);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task LockAsync(int id) public async Task LockAsync(int id)
{ {
var job = await _context.Jobs.FindAsync(id); await using var context = _factory.CreateDbContext();
var job = await context.Jobs.FindAsync(id);
if (job != null) if (job != null)
{ {
job.LockedAt = DateTime.UtcNow; job.LockedAt = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task UnlockAsync(int id) public async Task UnlockAsync(int id)
{ {
var job = await _context.Jobs.FindAsync(id); await using var context = _factory.CreateDbContext();
var job = await context.Jobs.FindAsync(id);
if (job != null) if (job != null)
{ {
job.LockedAt = null; job.LockedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task DeleteAsync(int id) public async Task DeleteAsync(int id)
{ {
var job = await _context.Jobs.FindAsync(id); await using var context = _factory.CreateDbContext();
var job = await context.Jobs.FindAsync(id);
if (job != null) if (job != null)
{ {
_context.Jobs.Remove(job); context.Jobs.Remove(job);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task<Job> DuplicateAsync(int id) public async Task<Job> DuplicateAsync(int id)
{ {
var original = await GetByIdAsync(id); await using var context = _factory.CreateDbContext();
var original = await context.Jobs
.Include(p => p.CuttingTool)
.Include(p => p.Parts.OrderBy(pt => pt.SortOrder))
.ThenInclude(pt => pt.Material)
.Include(p => p.Stock.OrderBy(s => s.SortOrder))
.ThenInclude(s => s.Material)
.Include(p => p.Stock)
.ThenInclude(s => s.StockItem)
.FirstOrDefaultAsync(p => p.Id == id);
if (original == null) if (original == null)
{ {
throw new ArgumentException("Job not found", nameof(id)); throw new ArgumentException("Job not found", nameof(id));
@@ -118,7 +142,7 @@ public class JobService
var duplicate = new Job var duplicate = new Job
{ {
JobNumber = await GenerateJobNumberAsync(), JobNumber = await GenerateJobNumberAsync(context),
Name = string.IsNullOrWhiteSpace(original.Name) ? null : $"{original.Name} (Copy)", Name = string.IsNullOrWhiteSpace(original.Name) ? null : $"{original.Name} (Copy)",
Customer = original.Customer, Customer = original.Customer,
CuttingToolId = original.CuttingToolId, CuttingToolId = original.CuttingToolId,
@@ -126,13 +150,13 @@ public class JobService
CreatedAt = DateTime.UtcNow CreatedAt = DateTime.UtcNow
}; };
_context.Jobs.Add(duplicate); context.Jobs.Add(duplicate);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
// Copy parts // Copy parts
foreach (var part in original.Parts) foreach (var part in original.Parts)
{ {
_context.JobParts.Add(new JobPart context.JobParts.Add(new JobPart
{ {
JobId = duplicate.Id, JobId = duplicate.Id,
MaterialId = part.MaterialId, MaterialId = part.MaterialId,
@@ -146,7 +170,7 @@ public class JobService
// Copy stock selections // Copy stock selections
foreach (var stock in original.Stock) foreach (var stock in original.Stock)
{ {
_context.JobStocks.Add(new JobStock context.JobStocks.Add(new JobStock
{ {
JobId = duplicate.Id, JobId = duplicate.Id,
MaterialId = stock.MaterialId, MaterialId = stock.MaterialId,
@@ -159,52 +183,55 @@ public class JobService
}); });
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return duplicate; return duplicate;
} }
// Optimization result persistence // Optimization result persistence
public async Task SaveOptimizationResultAsync(int jobId, string resultJson, DateTime optimizedAt) public async Task SaveOptimizationResultAsync(int jobId, string resultJson, DateTime optimizedAt)
{ {
var job = await _context.Jobs.FindAsync(jobId); await using var context = _factory.CreateDbContext();
var job = await context.Jobs.FindAsync(jobId);
if (job != null) if (job != null)
{ {
job.OptimizationResultJson = resultJson; job.OptimizationResultJson = resultJson;
job.OptimizedAt = optimizedAt; job.OptimizedAt = optimizedAt;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task ClearOptimizationResultAsync(int jobId) public async Task ClearOptimizationResultAsync(int jobId)
{ {
var job = await _context.Jobs.FindAsync(jobId); await using var context = _factory.CreateDbContext();
var job = await context.Jobs.FindAsync(jobId);
if (job != null && job.OptimizationResultJson != null) if (job != null && job.OptimizationResultJson != null)
{ {
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
// Parts management // Parts management
public async Task<JobPart> AddPartAsync(JobPart part) public async Task<JobPart> AddPartAsync(JobPart part)
{ {
var maxOrder = await _context.JobParts await using var context = _factory.CreateDbContext();
var maxOrder = await context.JobParts
.Where(p => p.JobId == part.JobId) .Where(p => p.JobId == part.JobId)
.MaxAsync(p => (int?)p.SortOrder) ?? -1; .MaxAsync(p => (int?)p.SortOrder) ?? -1;
part.SortOrder = maxOrder + 1; part.SortOrder = maxOrder + 1;
_context.JobParts.Add(part); context.JobParts.Add(part);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
// Update job timestamp and clear stale results // Update job timestamp and clear stale results
var job = await _context.Jobs.FindAsync(part.JobId); var job = await context.Jobs.FindAsync(part.JobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
return part; return part;
@@ -212,35 +239,37 @@ public class JobService
public async Task UpdatePartAsync(JobPart part) public async Task UpdatePartAsync(JobPart part)
{ {
_context.JobParts.Update(part); await using var context = _factory.CreateDbContext();
await _context.SaveChangesAsync(); context.JobParts.Update(part);
await context.SaveChangesAsync();
var job = await _context.Jobs.FindAsync(part.JobId); var job = await context.Jobs.FindAsync(part.JobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task DeletePartAsync(int id) public async Task DeletePartAsync(int id)
{ {
var part = await _context.JobParts.FindAsync(id); await using var context = _factory.CreateDbContext();
var part = await context.JobParts.FindAsync(id);
if (part != null) if (part != null)
{ {
var jobId = part.JobId; var jobId = part.JobId;
_context.JobParts.Remove(part); context.JobParts.Remove(part);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
var job = await _context.Jobs.FindAsync(jobId); var job = await context.Jobs.FindAsync(jobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
} }
@@ -248,21 +277,22 @@ public class JobService
// Stock management // Stock management
public async Task<JobStock> AddStockAsync(JobStock stock) public async Task<JobStock> AddStockAsync(JobStock stock)
{ {
var maxOrder = await _context.JobStocks await using var context = _factory.CreateDbContext();
var maxOrder = await context.JobStocks
.Where(s => s.JobId == stock.JobId) .Where(s => s.JobId == stock.JobId)
.MaxAsync(s => (int?)s.SortOrder) ?? -1; .MaxAsync(s => (int?)s.SortOrder) ?? -1;
stock.SortOrder = maxOrder + 1; stock.SortOrder = maxOrder + 1;
_context.JobStocks.Add(stock); context.JobStocks.Add(stock);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
var job = await _context.Jobs.FindAsync(stock.JobId); var job = await context.Jobs.FindAsync(stock.JobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
return stock; return stock;
@@ -270,42 +300,45 @@ public class JobService
public async Task UpdateStockAsync(JobStock stock) public async Task UpdateStockAsync(JobStock stock)
{ {
_context.JobStocks.Update(stock); await using var context = _factory.CreateDbContext();
await _context.SaveChangesAsync(); context.JobStocks.Update(stock);
await context.SaveChangesAsync();
var job = await _context.Jobs.FindAsync(stock.JobId); var job = await context.Jobs.FindAsync(stock.JobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task DeleteStockAsync(int id) public async Task DeleteStockAsync(int id)
{ {
var stock = await _context.JobStocks.FindAsync(id); await using var context = _factory.CreateDbContext();
var stock = await context.JobStocks.FindAsync(id);
if (stock != null) if (stock != null)
{ {
var jobId = stock.JobId; var jobId = stock.JobId;
_context.JobStocks.Remove(stock); context.JobStocks.Remove(stock);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
var job = await _context.Jobs.FindAsync(jobId); var job = await context.Jobs.FindAsync(jobId);
if (job != null) if (job != null)
{ {
job.UpdatedAt = DateTime.UtcNow; job.UpdatedAt = DateTime.UtcNow;
job.OptimizationResultJson = null; job.OptimizationResultJson = null;
job.OptimizedAt = null; job.OptimizedAt = null;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
} }
public async Task<List<StockItem>> GetAvailableStockForMaterialAsync(int materialId) public async Task<List<StockItem>> GetAvailableStockForMaterialAsync(int materialId)
{ {
return await _context.StockItems await using var context = _factory.CreateDbContext();
return await context.StockItems
.Include(s => s.Material) .Include(s => s.Material)
.Where(s => s.MaterialId == materialId && s.IsActive) .Where(s => s.MaterialId == materialId && s.IsActive)
.OrderBy(s => s.LengthInches) .OrderBy(s => s.LengthInches)
@@ -315,7 +348,8 @@ public class JobService
// Cutting tools // Cutting tools
public async Task<List<CuttingTool>> GetCuttingToolsAsync(bool includeInactive = false) public async Task<List<CuttingTool>> GetCuttingToolsAsync(bool includeInactive = false)
{ {
var query = _context.CuttingTools.AsQueryable(); await using var context = _factory.CreateDbContext();
var query = context.CuttingTools.AsQueryable();
if (!includeInactive) if (!includeInactive)
{ {
query = query.Where(t => t.IsActive); query = query.Where(t => t.IsActive);
@@ -325,53 +359,58 @@ public class JobService
public async Task<CuttingTool?> GetCuttingToolByIdAsync(int id) public async Task<CuttingTool?> GetCuttingToolByIdAsync(int id)
{ {
return await _context.CuttingTools.FindAsync(id); await using var context = _factory.CreateDbContext();
return await context.CuttingTools.FindAsync(id);
} }
public async Task<CuttingTool?> GetDefaultCuttingToolAsync() public async Task<CuttingTool?> GetDefaultCuttingToolAsync()
{ {
return await _context.CuttingTools.FirstOrDefaultAsync(t => t.IsDefault && t.IsActive); await using var context = _factory.CreateDbContext();
return await context.CuttingTools.FirstOrDefaultAsync(t => t.IsDefault && t.IsActive);
} }
public async Task<CuttingTool> CreateCuttingToolAsync(CuttingTool tool) public async Task<CuttingTool> CreateCuttingToolAsync(CuttingTool tool)
{ {
await using var context = _factory.CreateDbContext();
if (tool.IsDefault) if (tool.IsDefault)
{ {
// Clear other defaults // Clear other defaults
var others = await _context.CuttingTools.Where(t => t.IsDefault).ToListAsync(); var others = await context.CuttingTools.Where(t => t.IsDefault).ToListAsync();
foreach (var other in others) foreach (var other in others)
{ {
other.IsDefault = false; other.IsDefault = false;
} }
} }
_context.CuttingTools.Add(tool); context.CuttingTools.Add(tool);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return tool; return tool;
} }
public async Task UpdateCuttingToolAsync(CuttingTool tool) public async Task UpdateCuttingToolAsync(CuttingTool tool)
{ {
await using var context = _factory.CreateDbContext();
if (tool.IsDefault) if (tool.IsDefault)
{ {
var others = await _context.CuttingTools.Where(t => t.IsDefault && t.Id != tool.Id).ToListAsync(); var others = await context.CuttingTools.Where(t => t.IsDefault && t.Id != tool.Id).ToListAsync();
foreach (var other in others) foreach (var other in others)
{ {
other.IsDefault = false; other.IsDefault = false;
} }
} }
_context.CuttingTools.Update(tool); context.CuttingTools.Update(tool);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task DeleteCuttingToolAsync(int id) public async Task DeleteCuttingToolAsync(int id)
{ {
var tool = await _context.CuttingTools.FindAsync(id); await using var context = _factory.CreateDbContext();
var tool = await context.CuttingTools.FindAsync(id);
if (tool != null) if (tool != null)
{ {
tool.IsActive = false; tool.IsActive = false;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
} }
+52 -33
View File
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
public class MaterialService public class MaterialService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
public MaterialService(ApplicationDbContext context) public MaterialService(IDbContextFactory<ApplicationDbContext> factory)
{ {
_context = context; _factory = factory;
} }
public async Task<List<Material>> GetAllAsync(bool includeInactive = false) public async Task<List<Material>> GetAllAsync(bool includeInactive = false)
{ {
var query = _context.Materials await using var context = _factory.CreateDbContext();
var query = context.Materials
.Include(m => m.Dimensions) .Include(m => m.Dimensions)
.AsQueryable(); .AsQueryable();
if (!includeInactive) if (!includeInactive)
@@ -27,16 +28,18 @@ public class MaterialService
public async Task<Material?> GetByIdAsync(int id) public async Task<Material?> GetByIdAsync(int id)
{ {
return await _context.Materials await using var context = _factory.CreateDbContext();
return await context.Materials
.Include(m => m.Dimensions) .Include(m => m.Dimensions)
.FirstOrDefaultAsync(m => m.Id == id); .FirstOrDefaultAsync(m => m.Id == id);
} }
public async Task<Material> CreateAsync(Material material) public async Task<Material> CreateAsync(Material material)
{ {
await using var context = _factory.CreateDbContext();
material.CreatedAt = DateTime.UtcNow; material.CreatedAt = DateTime.UtcNow;
_context.Materials.Add(material); context.Materials.Add(material);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return material; return material;
} }
@@ -45,6 +48,7 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<Material> CreateWithDimensionsAsync(Material material, MaterialDimensions dimensions) public async Task<Material> CreateWithDimensionsAsync(Material material, MaterialDimensions dimensions)
{ {
await using var context = _factory.CreateDbContext();
material.CreatedAt = DateTime.UtcNow; material.CreatedAt = DateTime.UtcNow;
// Auto-generate Size string from dimensions if not provided // Auto-generate Size string from dimensions if not provided
@@ -56,13 +60,13 @@ public class MaterialService
// Set sort order from primary dimension // Set sort order from primary dimension
material.SortOrder = dimensions.GetSortOrder(); material.SortOrder = dimensions.GetSortOrder();
_context.Materials.Add(material); context.Materials.Add(material);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
// Link dimensions to the created material // Link dimensions to the created material
dimensions.MaterialId = material.Id; dimensions.MaterialId = material.Id;
_context.MaterialDimensions.Add(dimensions); context.MaterialDimensions.Add(dimensions);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
material.Dimensions = dimensions; material.Dimensions = dimensions;
return material; return material;
@@ -70,9 +74,10 @@ public class MaterialService
public async Task UpdateAsync(Material material) public async Task UpdateAsync(Material material)
{ {
await using var context = _factory.CreateDbContext();
material.UpdatedAt = DateTime.UtcNow; material.UpdatedAt = DateTime.UtcNow;
_context.Materials.Update(material); context.Materials.Update(material);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
/// <summary> /// <summary>
@@ -80,6 +85,7 @@ public class MaterialService
/// </summary> /// </summary>
public async Task UpdateWithDimensionsAsync(Material material, MaterialDimensions dimensions, bool regenerateSize = false) public async Task UpdateWithDimensionsAsync(Material material, MaterialDimensions dimensions, bool regenerateSize = false)
{ {
await using var context = _factory.CreateDbContext();
material.UpdatedAt = DateTime.UtcNow; material.UpdatedAt = DateTime.UtcNow;
if (regenerateSize) if (regenerateSize)
@@ -98,14 +104,14 @@ public class MaterialService
if (dimensions.Id > 0) if (dimensions.Id > 0)
{ {
// Already tracked, just save // Already tracked, just save
_context.Entry(material).State = EntityState.Modified; context.Entry(material).State = EntityState.Modified;
} }
else else
{ {
_context.Materials.Update(material); context.Materials.Update(material);
// Check if dimensions already exist for this material // Check if dimensions already exist for this material
var existingDimensions = await _context.MaterialDimensions var existingDimensions = await context.MaterialDimensions
.AsNoTracking() .AsNoTracking()
.FirstOrDefaultAsync(d => d.MaterialId == material.Id); .FirstOrDefaultAsync(d => d.MaterialId == material.Id);
@@ -113,30 +119,32 @@ public class MaterialService
{ {
// Copy the existing Id to update in place // Copy the existing Id to update in place
dimensions.Id = existingDimensions.Id; dimensions.Id = existingDimensions.Id;
_context.MaterialDimensions.Update(dimensions); context.MaterialDimensions.Update(dimensions);
} }
else else
{ {
_context.MaterialDimensions.Add(dimensions); context.MaterialDimensions.Add(dimensions);
} }
} }
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task DeleteAsync(int id) public async Task DeleteAsync(int id)
{ {
var material = await _context.Materials.FindAsync(id); await using var context = _factory.CreateDbContext();
var material = await context.Materials.FindAsync(id);
if (material != null) if (material != null)
{ {
material.IsActive = false; material.IsActive = false;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task<bool> ExistsAsync(MaterialShape shape, string size, int? excludeId = null) public async Task<bool> ExistsAsync(MaterialShape shape, string size, int? excludeId = null)
{ {
var query = _context.Materials.Where(m => m.Shape == shape && m.Size == size && m.IsActive); await using var context = _factory.CreateDbContext();
var query = context.Materials.Where(m => m.Shape == shape && m.Size == size && m.IsActive);
if (excludeId.HasValue) if (excludeId.HasValue)
{ {
query = query.Where(m => m.Id != excludeId.Value); query = query.Where(m => m.Id != excludeId.Value);
@@ -149,10 +157,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchRoundBarByDiameterAsync(decimal targetDiameter, decimal tolerance) public async Task<List<Material>> SearchRoundBarByDiameterAsync(decimal targetDiameter, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetDiameter - tolerance; var minValue = targetDiameter - tolerance;
var maxValue = targetDiameter + tolerance; var maxValue = targetDiameter + tolerance;
return await _context.Set<RoundBarDimensions>() return await context.Set<RoundBarDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Diameter >= minValue && d.Diameter <= maxValue) .Where(d => d.Diameter >= minValue && d.Diameter <= maxValue)
@@ -166,10 +175,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchRoundTubeByODAsync(decimal targetOD, decimal tolerance) public async Task<List<Material>> SearchRoundTubeByODAsync(decimal targetOD, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetOD - tolerance; var minValue = targetOD - tolerance;
var maxValue = targetOD + tolerance; var maxValue = targetOD + tolerance;
return await _context.Set<RoundTubeDimensions>() return await context.Set<RoundTubeDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.OuterDiameter >= minValue && d.OuterDiameter <= maxValue) .Where(d => d.OuterDiameter >= minValue && d.OuterDiameter <= maxValue)
@@ -183,10 +193,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchFlatBarByWidthAsync(decimal targetWidth, decimal tolerance) public async Task<List<Material>> SearchFlatBarByWidthAsync(decimal targetWidth, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetWidth - tolerance; var minValue = targetWidth - tolerance;
var maxValue = targetWidth + tolerance; var maxValue = targetWidth + tolerance;
return await _context.Set<FlatBarDimensions>() return await context.Set<FlatBarDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Width >= minValue && d.Width <= maxValue) .Where(d => d.Width >= minValue && d.Width <= maxValue)
@@ -200,10 +211,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchSquareBarBySizeAsync(decimal targetSize, decimal tolerance) public async Task<List<Material>> SearchSquareBarBySizeAsync(decimal targetSize, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetSize - tolerance; var minValue = targetSize - tolerance;
var maxValue = targetSize + tolerance; var maxValue = targetSize + tolerance;
return await _context.Set<SquareBarDimensions>() return await context.Set<SquareBarDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Size >= minValue && d.Size <= maxValue) .Where(d => d.Size >= minValue && d.Size <= maxValue)
@@ -217,10 +229,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchSquareTubeBySizeAsync(decimal targetSize, decimal tolerance) public async Task<List<Material>> SearchSquareTubeBySizeAsync(decimal targetSize, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetSize - tolerance; var minValue = targetSize - tolerance;
var maxValue = targetSize + tolerance; var maxValue = targetSize + tolerance;
return await _context.Set<SquareTubeDimensions>() return await context.Set<SquareTubeDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Size >= minValue && d.Size <= maxValue) .Where(d => d.Size >= minValue && d.Size <= maxValue)
@@ -234,10 +247,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchRectangularTubeByWidthAsync(decimal targetWidth, decimal tolerance) public async Task<List<Material>> SearchRectangularTubeByWidthAsync(decimal targetWidth, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetWidth - tolerance; var minValue = targetWidth - tolerance;
var maxValue = targetWidth + tolerance; var maxValue = targetWidth + tolerance;
return await _context.Set<RectangularTubeDimensions>() return await context.Set<RectangularTubeDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Width >= minValue && d.Width <= maxValue) .Where(d => d.Width >= minValue && d.Width <= maxValue)
@@ -251,10 +265,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchAngleByLegAsync(decimal targetLeg, decimal tolerance) public async Task<List<Material>> SearchAngleByLegAsync(decimal targetLeg, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetLeg - tolerance; var minValue = targetLeg - tolerance;
var maxValue = targetLeg + tolerance; var maxValue = targetLeg + tolerance;
return await _context.Set<AngleDimensions>() return await context.Set<AngleDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Leg1 >= minValue && d.Leg1 <= maxValue) .Where(d => d.Leg1 >= minValue && d.Leg1 <= maxValue)
@@ -268,10 +283,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchChannelByHeightAsync(decimal targetHeight, decimal tolerance) public async Task<List<Material>> SearchChannelByHeightAsync(decimal targetHeight, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetHeight - tolerance; var minValue = targetHeight - tolerance;
var maxValue = targetHeight + tolerance; var maxValue = targetHeight + tolerance;
return await _context.Set<ChannelDimensions>() return await context.Set<ChannelDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Height >= minValue && d.Height <= maxValue) .Where(d => d.Height >= minValue && d.Height <= maxValue)
@@ -285,10 +301,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchIBeamByHeightAsync(decimal targetHeight, decimal tolerance) public async Task<List<Material>> SearchIBeamByHeightAsync(decimal targetHeight, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetHeight - tolerance; var minValue = targetHeight - tolerance;
var maxValue = targetHeight + tolerance; var maxValue = targetHeight + tolerance;
return await _context.Set<IBeamDimensions>() return await context.Set<IBeamDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.Height >= minValue && d.Height <= maxValue) .Where(d => d.Height >= minValue && d.Height <= maxValue)
@@ -302,10 +319,11 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> SearchPipeByNominalSizeAsync(decimal targetNPS, decimal tolerance) public async Task<List<Material>> SearchPipeByNominalSizeAsync(decimal targetNPS, decimal tolerance)
{ {
await using var context = _factory.CreateDbContext();
var minValue = targetNPS - tolerance; var minValue = targetNPS - tolerance;
var maxValue = targetNPS + tolerance; var maxValue = targetNPS + tolerance;
return await _context.Set<PipeDimensions>() return await context.Set<PipeDimensions>()
.Include(d => d.Material) .Include(d => d.Material)
.Where(d => d.Material.IsActive) .Where(d => d.Material.IsActive)
.Where(d => d.NominalSize >= minValue && d.NominalSize <= maxValue) .Where(d => d.NominalSize >= minValue && d.NominalSize <= maxValue)
@@ -319,7 +337,8 @@ public class MaterialService
/// </summary> /// </summary>
public async Task<List<Material>> GetByShapeAsync(MaterialShape shape, bool includeInactive = false) public async Task<List<Material>> GetByShapeAsync(MaterialShape shape, bool includeInactive = false)
{ {
var query = _context.Materials await using var context = _factory.CreateDbContext();
var query = context.Materials
.Include(m => m.Dimensions) .Include(m => m.Dimensions)
.Where(m => m.Shape == shape); .Where(m => m.Shape == shape);
+34 -18
View File
@@ -6,16 +6,18 @@ namespace CutList.Web.Services;
public class PurchaseItemService public class PurchaseItemService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
public PurchaseItemService(ApplicationDbContext context) public PurchaseItemService(IDbContextFactory<ApplicationDbContext> factory)
{ {
_context = context; _factory = factory;
} }
public async Task<List<PurchaseItem>> GetAllAsync(PurchaseItemStatus? status = null) public async Task<List<PurchaseItem>> GetAllAsync(PurchaseItemStatus? status = null)
{ {
var query = _context.PurchaseItems await using var context = _factory.CreateDbContext();
var query = context.PurchaseItems
.Include(p => p.StockItem) .Include(p => p.StockItem)
.ThenInclude(s => s.Material) .ThenInclude(s => s.Material)
.Include(p => p.Supplier) .Include(p => p.Supplier)
@@ -35,7 +37,9 @@ public class PurchaseItemService
public async Task<PurchaseItem?> GetByIdAsync(int id) public async Task<PurchaseItem?> GetByIdAsync(int id)
{ {
return await _context.PurchaseItems await using var context = _factory.CreateDbContext();
return await context.PurchaseItems
.Include(p => p.StockItem) .Include(p => p.StockItem)
.ThenInclude(s => s.Material) .ThenInclude(s => s.Material)
.Include(p => p.Supplier) .Include(p => p.Supplier)
@@ -45,59 +49,71 @@ public class PurchaseItemService
public async Task<PurchaseItem> CreateAsync(PurchaseItem item) public async Task<PurchaseItem> CreateAsync(PurchaseItem item)
{ {
await using var context = _factory.CreateDbContext();
item.CreatedAt = DateTime.UtcNow; item.CreatedAt = DateTime.UtcNow;
_context.PurchaseItems.Add(item); context.PurchaseItems.Add(item);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return item; return item;
} }
public async Task CreateBulkAsync(List<PurchaseItem> items) public async Task CreateBulkAsync(List<PurchaseItem> items)
{ {
await using var context = _factory.CreateDbContext();
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
foreach (var item in items) foreach (var item in items)
{ {
item.CreatedAt = now; item.CreatedAt = now;
} }
_context.PurchaseItems.AddRange(items); context.PurchaseItems.AddRange(items);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task UpdateAsync(PurchaseItem item) public async Task UpdateAsync(PurchaseItem item)
{ {
await using var context = _factory.CreateDbContext();
item.UpdatedAt = DateTime.UtcNow; item.UpdatedAt = DateTime.UtcNow;
_context.PurchaseItems.Update(item); context.PurchaseItems.Update(item);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task UpdateStatusAsync(int id, PurchaseItemStatus status) public async Task UpdateStatusAsync(int id, PurchaseItemStatus status)
{ {
var item = await _context.PurchaseItems.FindAsync(id); await using var context = _factory.CreateDbContext();
var item = await context.PurchaseItems.FindAsync(id);
if (item != null) if (item != null)
{ {
item.Status = status; item.Status = status;
item.UpdatedAt = DateTime.UtcNow; item.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task UpdateSupplierAsync(int id, int? supplierId) public async Task UpdateSupplierAsync(int id, int? supplierId)
{ {
var item = await _context.PurchaseItems.FindAsync(id); await using var context = _factory.CreateDbContext();
var item = await context.PurchaseItems.FindAsync(id);
if (item != null) if (item != null)
{ {
item.SupplierId = supplierId; item.SupplierId = supplierId;
item.UpdatedAt = DateTime.UtcNow; item.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task DeleteAsync(int id) public async Task DeleteAsync(int id)
{ {
var item = await _context.PurchaseItems.FindAsync(id); await using var context = _factory.CreateDbContext();
var item = await context.PurchaseItems.FindAsync(id);
if (item != null) if (item != null)
{ {
_context.PurchaseItems.Remove(item); context.PurchaseItems.Remove(item);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
} }
+61 -31
View File
@@ -6,16 +6,18 @@ namespace CutList.Web.Services;
public class StockItemService 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) 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) .Include(s => s.Material)
.AsQueryable(); .AsQueryable();
@@ -33,7 +35,9 @@ public class StockItemService
public async Task<List<StockItem>> GetByMaterialAsync(int materialId, bool includeInactive = false) 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) .Include(s => s.Material)
.Where(s => s.MaterialId == materialId); .Where(s => s.MaterialId == materialId);
@@ -49,7 +53,9 @@ public class StockItemService
public async Task<StockItem?> GetByIdAsync(int id) 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.Material)
.Include(s => s.SupplierOfferings) .Include(s => s.SupplierOfferings)
.ThenInclude(o => o.Supplier) .ThenInclude(o => o.Supplier)
@@ -58,33 +64,41 @@ public class StockItemService
public async Task<StockItem> CreateAsync(StockItem stockItem) public async Task<StockItem> CreateAsync(StockItem stockItem)
{ {
await using var context = _factory.CreateDbContext();
stockItem.CreatedAt = DateTime.UtcNow; stockItem.CreatedAt = DateTime.UtcNow;
_context.StockItems.Add(stockItem); context.StockItems.Add(stockItem);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return stockItem; return stockItem;
} }
public async Task UpdateAsync(StockItem stockItem) public async Task UpdateAsync(StockItem stockItem)
{ {
await using var context = _factory.CreateDbContext();
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
_context.StockItems.Update(stockItem); context.StockItems.Update(stockItem);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
public async Task DeleteAsync(int id) 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) if (stockItem != null)
{ {
stockItem.IsActive = false; stockItem.IsActive = false;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task<bool> ExistsAsync(int materialId, decimal lengthInches, int? excludeId = null) 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.MaterialId == materialId &&
s.LengthInches == lengthInches && s.LengthInches == lengthInches &&
s.IsActive); s.IsActive);
@@ -100,7 +114,9 @@ public class StockItemService
// Stock transaction methods // Stock transaction methods
public async Task<StockTransaction> AddStockAsync(int stockItemId, int quantity, int? supplierId = null, decimal? unitPrice = null, string? notes = null) 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"); ?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
var transaction = new StockTransaction var transaction = new StockTransaction
@@ -117,15 +133,17 @@ public class StockItemService
stockItem.QuantityOnHand += quantity; stockItem.QuantityOnHand += quantity;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
_context.StockTransactions.Add(transaction); context.StockTransactions.Add(transaction);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return transaction; return transaction;
} }
public async Task<decimal?> GetAverageCostAsync(int stockItemId) 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) .Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
.ToListAsync(); .ToListAsync();
@@ -140,7 +158,9 @@ public class StockItemService
public async Task<decimal?> GetLastPurchasePriceAsync(int stockItemId) 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) .Where(t => t.StockItemId == stockItemId && t.Type == StockTransactionType.Received && t.UnitPrice.HasValue)
.OrderByDescending(t => t.CreatedAt) .OrderByDescending(t => t.CreatedAt)
.Select(t => t.UnitPrice) .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) 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"); ?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
var transaction = new StockTransaction var transaction = new StockTransaction
@@ -165,15 +187,17 @@ public class StockItemService
stockItem.QuantityOnHand -= quantity; stockItem.QuantityOnHand -= quantity;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
_context.StockTransactions.Add(transaction); context.StockTransactions.Add(transaction);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return transaction; return transaction;
} }
public async Task<StockTransaction> AdjustStockAsync(int stockItemId, int newQuantity, string? notes = null) 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"); ?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
var difference = newQuantity - stockItem.QuantityOnHand; var difference = newQuantity - stockItem.QuantityOnHand;
@@ -190,15 +214,17 @@ public class StockItemService
stockItem.QuantityOnHand = newQuantity; stockItem.QuantityOnHand = newQuantity;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
_context.StockTransactions.Add(transaction); context.StockTransactions.Add(transaction);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return transaction; return transaction;
} }
public async Task<StockTransaction> ScrapStockAsync(int stockItemId, int quantity, string? notes = null) 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"); ?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
var transaction = new StockTransaction var transaction = new StockTransaction
@@ -213,15 +239,17 @@ public class StockItemService
stockItem.QuantityOnHand -= quantity; stockItem.QuantityOnHand -= quantity;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
_context.StockTransactions.Add(transaction); context.StockTransactions.Add(transaction);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return transaction; return transaction;
} }
public async Task<List<StockTransaction>> GetTransactionHistoryAsync(int stockItemId, int? limit = null) 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.Job)
.Include(t => t.Supplier) .Include(t => t.Supplier)
.Where(t => t.StockItemId == stockItemId) .Where(t => t.StockItemId == stockItemId)
@@ -238,10 +266,12 @@ public class StockItemService
public async Task<int> RecalculateQuantityAsync(int stockItemId) 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"); ?? throw new InvalidOperationException($"Stock item {stockItemId} not found");
var calculatedQuantity = await _context.StockTransactions var calculatedQuantity = await context.StockTransactions
.Where(t => t.StockItemId == stockItemId) .Where(t => t.StockItemId == stockItemId)
.SumAsync(t => t.Quantity); .SumAsync(t => t.Quantity);
@@ -249,7 +279,7 @@ public class StockItemService
{ {
stockItem.QuantityOnHand = calculatedQuantity; stockItem.QuantityOnHand = calculatedQuantity;
stockItem.UpdatedAt = DateTime.UtcNow; stockItem.UpdatedAt = DateTime.UtcNow;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
return calculatedQuantity; return calculatedQuantity;
+33 -21
View File
@@ -6,16 +6,17 @@ namespace CutList.Web.Services;
public class SupplierService public class SupplierService
{ {
private readonly ApplicationDbContext _context; private readonly IDbContextFactory<ApplicationDbContext> _factory;
public SupplierService(ApplicationDbContext context) public SupplierService(IDbContextFactory<ApplicationDbContext> factory)
{ {
_context = context; _factory = factory;
} }
public async Task<List<Supplier>> GetAllAsync(bool includeInactive = false) public async Task<List<Supplier>> GetAllAsync(bool includeInactive = false)
{ {
var query = _context.Suppliers.AsQueryable(); await using var context = _factory.CreateDbContext();
var query = context.Suppliers.AsQueryable();
if (!includeInactive) if (!includeInactive)
{ {
query = query.Where(s => s.IsActive); query = query.Where(s => s.IsActive);
@@ -25,7 +26,8 @@ public class SupplierService
public async Task<Supplier?> GetByIdAsync(int id) public async Task<Supplier?> GetByIdAsync(int id)
{ {
return await _context.Suppliers await using var context = _factory.CreateDbContext();
return await context.Suppliers
.Include(s => s.Offerings) .Include(s => s.Offerings)
.ThenInclude(o => o.StockItem) .ThenInclude(o => o.StockItem)
.ThenInclude(si => si.Material) .ThenInclude(si => si.Material)
@@ -34,32 +36,36 @@ public class SupplierService
public async Task<Supplier> CreateAsync(Supplier supplier) public async Task<Supplier> CreateAsync(Supplier supplier)
{ {
await using var context = _factory.CreateDbContext();
supplier.CreatedAt = DateTime.UtcNow; supplier.CreatedAt = DateTime.UtcNow;
_context.Suppliers.Add(supplier); context.Suppliers.Add(supplier);
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
return supplier; return supplier;
} }
public async Task UpdateAsync(Supplier supplier) public async Task UpdateAsync(Supplier supplier)
{ {
_context.Suppliers.Update(supplier); await using var context = _factory.CreateDbContext();
await _context.SaveChangesAsync(); context.Suppliers.Update(supplier);
await context.SaveChangesAsync();
} }
public async Task DeleteAsync(int id) public async Task DeleteAsync(int id)
{ {
var supplier = await _context.Suppliers.FindAsync(id); await using var context = _factory.CreateDbContext();
var supplier = await context.Suppliers.FindAsync(id);
if (supplier != null) if (supplier != null)
{ {
supplier.IsActive = false; supplier.IsActive = false;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
// Offering management // Offering management
public async Task<List<SupplierOffering>> GetOfferingsForSupplierAsync(int supplierId) public async Task<List<SupplierOffering>> GetOfferingsForSupplierAsync(int supplierId)
{ {
return await _context.SupplierOfferings await using var context = _factory.CreateDbContext();
return await context.SupplierOfferings
.Include(o => o.StockItem) .Include(o => o.StockItem)
.ThenInclude(si => si.Material) .ThenInclude(si => si.Material)
.Where(o => o.SupplierId == supplierId && o.IsActive) .Where(o => o.SupplierId == supplierId && o.IsActive)
@@ -71,7 +77,8 @@ public class SupplierService
public async Task<List<SupplierOffering>> GetOfferingsForStockItemAsync(int stockItemId) public async Task<List<SupplierOffering>> GetOfferingsForStockItemAsync(int stockItemId)
{ {
return await _context.SupplierOfferings await using var context = _factory.CreateDbContext();
return await context.SupplierOfferings
.Include(o => o.Supplier) .Include(o => o.Supplier)
.Where(o => o.StockItemId == stockItemId && o.IsActive && o.Supplier.IsActive) .Where(o => o.StockItemId == stockItemId && o.IsActive && o.Supplier.IsActive)
.OrderBy(o => o.Supplier.Name) .OrderBy(o => o.Supplier.Name)
@@ -80,7 +87,8 @@ public class SupplierService
public async Task<SupplierOffering?> GetOfferingByIdAsync(int id) public async Task<SupplierOffering?> GetOfferingByIdAsync(int id)
{ {
return await _context.SupplierOfferings await using var context = _factory.CreateDbContext();
return await context.SupplierOfferings
.Include(o => o.StockItem) .Include(o => o.StockItem)
.ThenInclude(si => si.Material) .ThenInclude(si => si.Material)
.Include(o => o.Supplier) .Include(o => o.Supplier)
@@ -89,30 +97,34 @@ public class SupplierService
public async Task<SupplierOffering> AddOfferingAsync(SupplierOffering offering) public async Task<SupplierOffering> AddOfferingAsync(SupplierOffering offering)
{ {
_context.SupplierOfferings.Add(offering); await using var context = _factory.CreateDbContext();
await _context.SaveChangesAsync(); context.SupplierOfferings.Add(offering);
await context.SaveChangesAsync();
return offering; return offering;
} }
public async Task UpdateOfferingAsync(SupplierOffering offering) public async Task UpdateOfferingAsync(SupplierOffering offering)
{ {
_context.SupplierOfferings.Update(offering); await using var context = _factory.CreateDbContext();
await _context.SaveChangesAsync(); context.SupplierOfferings.Update(offering);
await context.SaveChangesAsync();
} }
public async Task DeleteOfferingAsync(int id) public async Task DeleteOfferingAsync(int id)
{ {
var offering = await _context.SupplierOfferings.FindAsync(id); await using var context = _factory.CreateDbContext();
var offering = await context.SupplierOfferings.FindAsync(id);
if (offering != null) if (offering != null)
{ {
offering.IsActive = false; offering.IsActive = false;
await _context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }
public async Task<bool> OfferingExistsAsync(int supplierId, int stockItemId, int? excludeId = null) public async Task<bool> OfferingExistsAsync(int supplierId, int stockItemId, int? excludeId = null)
{ {
var query = _context.SupplierOfferings.Where(o => await using var context = _factory.CreateDbContext();
var query = context.SupplierOfferings.Where(o =>
o.SupplierId == supplierId && o.SupplierId == supplierId &&
o.StockItemId == stockItemId && o.StockItemId == stockItemId &&
o.IsActive); o.IsActive);