Fix error loading Add/Edit Transaction Item page
All checks were successful
Docker Image CI / build (push) Successful in 3m8s

This commit is contained in:
James Chapman 2024-10-12 16:00:32 +01:00
parent 47d13ba922
commit 2c0f6f1cab
Signed by: jamsch0
GPG Key ID: 765FE58130277547
17 changed files with 99 additions and 41 deletions

View File

@ -4,7 +4,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
@inject IHttpContextAccessor HttpContextAccessor @inject IHttpContextAccessor HttpContextAccessor
<HeadContent> <HeadContent>
@ -52,7 +52,8 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
model = await DbContext.ItemTagQuantities using var dbContext = DbContextFactory.CreateDbContext();
model = await dbContext.ItemTagQuantities
.FromSqlRaw(@" .FromSqlRaw(@"
SELECT tag, quantity, coalesce(unit_name, unit) AS unit, is_metric, is_divisible SELECT tag, quantity, coalesce(unit_name, unit) AS unit, is_metric, is_divisible
FROM ( FROM (

View File

@ -2,7 +2,9 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@layout Layout @layout Layout
@inject AppDbContext DbContext
@implements IDisposable
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; Items</PageTitle> <PageTitle>Groceries &ndash; Items</PageTitle>
@ -44,6 +46,7 @@
public DateTime? LastPurchasedAt { get; init; } public DateTime? LastPurchasedAt { get; init; }
} }
private AppDbContext? dbContext;
private IQueryable<ItemModel> items = null!; private IQueryable<ItemModel> items = null!;
private PaginationState pagination = new(); private PaginationState pagination = new();
@ -52,7 +55,9 @@
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
var itemsQuery = DbContext.Items.AsQueryable(); dbContext ??= DbContextFactory.CreateDbContext();
var itemsQuery = dbContext.Items.AsQueryable();
if (!string.IsNullOrEmpty(Search)) if (!string.IsNullOrEmpty(Search))
{ {
var searchPattern = $"%{Search}%"; var searchPattern = $"%{Search}%";
@ -61,7 +66,7 @@
items = itemsQuery items = itemsQuery
.GroupJoin( .GroupJoin(
DbContext.ItemPurchases.Where(purchase => purchase.IsLastPurchase), dbContext.ItemPurchases.Where(purchase => purchase.IsLastPurchase),
item => item.Id, item => item.Id,
purchase => purchase.ItemId, purchase => purchase.ItemId,
(item, purchases) => new { item, purchases }) (item, purchases) => new { item, purchases })
@ -78,4 +83,9 @@
.OrderBy(item => item.Brand) .OrderBy(item => item.Brand)
.ThenBy(item => item.Name); .ThenBy(item => item.Name);
} }
public void Dispose()
{
dbContext?.Dispose();
}
} }

View File

@ -42,7 +42,7 @@ builder.Services.AddDistributedMemoryCache();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddSession(); builder.Services.AddSession();
builder.Services.AddDbContextPool<AppDbContext>(options => options builder.Services.AddPooledDbContextFactory<AppDbContext>(options => options
.EnableDetailedErrors(env.IsDevelopment()) .EnableDetailedErrors(env.IsDevelopment())
.EnableSensitiveDataLogging(env.IsDevelopment()) .EnableSensitiveDataLogging(env.IsDevelopment())
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)

View File

@ -1,7 +1,7 @@
@using Groceries.Data @using Groceries.Data
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<form method="post" @attributes="AdditionalAttributes"> <form method="post" @attributes="AdditionalAttributes">
<div class="form-field"> <div class="form-field">
@ -45,7 +45,8 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
retailers = await DbContext.Retailers using var dbContext = DbContextFactory.CreateDbContext();
retailers = await dbContext.Retailers
.OrderBy(retailer => retailer.Name) .OrderBy(retailer => retailer.Name)
.ToArrayAsync(); .ToArrayAsync();
} }

View File

@ -8,11 +8,11 @@ using Microsoft.EntityFrameworkCore;
[Route("/stores")] [Route("/stores")]
public class StoresController : Controller public class StoresController : Controller
{ {
private readonly AppDbContext dbContext; private readonly IDbContextFactory<AppDbContext> dbContextFactory;
public StoresController(AppDbContext dbContext) public StoresController(IDbContextFactory<AppDbContext> dbContextFactory)
{ {
this.dbContext = dbContext; this.dbContextFactory = dbContextFactory;
} }
[HttpGet] [HttpGet]
@ -32,6 +32,8 @@ public class StoresController : Controller
[HttpPost("new")] [HttpPost("new")]
public async Task<IResult> NewStore(Guid retailerId, string name, string? address) public async Task<IResult> NewStore(Guid retailerId, string name, string? address)
{ {
using var dbContext = dbContextFactory.CreateDbContext();
var store = new Store(retailerId, name, address); var store = new Store(retailerId, name, address);
dbContext.Stores.Add(store); dbContext.Stores.Add(store);
@ -45,6 +47,8 @@ public class StoresController : Controller
[HttpGet("edit/{id}")] [HttpGet("edit/{id}")]
public async Task<IResult> EditStore(Guid id) public async Task<IResult> EditStore(Guid id)
{ {
using var dbContext = dbContextFactory.CreateDbContext();
var store = await dbContext.Stores var store = await dbContext.Stores
.SingleOrDefaultAsync(store => store.Id == id, HttpContext.RequestAborted); .SingleOrDefaultAsync(store => store.Id == id, HttpContext.RequestAborted);
@ -61,6 +65,8 @@ public class StoresController : Controller
[HttpPost("edit/{id}")] [HttpPost("edit/{id}")]
public async Task<IResult> EditStore(Guid id, Guid retailerId, string name, string? address, string? returnUrl) public async Task<IResult> EditStore(Guid id, Guid retailerId, string name, string? address, string? returnUrl)
{ {
using var dbContext = dbContextFactory.CreateDbContext();
var store = new Store(id, retailerId, name, address); var store = new Store(id, retailerId, name, address);
dbContext.Stores.Update(store); dbContext.Stores.Update(store);

View File

@ -2,7 +2,9 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@layout Layout @layout Layout
@inject AppDbContext DbContext
@implements IDisposable
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; Stores</PageTitle> <PageTitle>Groceries &ndash; Stores</PageTitle>
@ -39,6 +41,7 @@
public int TransactionsCount { get; init; } public int TransactionsCount { get; init; }
} }
private AppDbContext? dbContext;
private IQueryable<StoreModel> stores = null!; private IQueryable<StoreModel> stores = null!;
private PaginationState pagination = new(); private PaginationState pagination = new();
@ -47,7 +50,9 @@
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
var storesQuery = DbContext.Stores.AsQueryable(); dbContext ??= DbContextFactory.CreateDbContext();
var storesQuery = dbContext.Stores.AsQueryable();
if (!string.IsNullOrEmpty(Search)) if (!string.IsNullOrEmpty(Search))
{ {
var searchPattern = $"%{Search}%"; var searchPattern = $"%{Search}%";
@ -66,4 +71,9 @@
.OrderBy(store => store.Retailer) .OrderBy(store => store.Retailer)
.ThenBy(store => store.Name); .ThenBy(store => store.Name);
} }
public void Dispose()
{
dbContext?.Dispose();
}
} }

View File

@ -3,7 +3,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; Edit Transaction Item</PageTitle> <PageTitle>Groceries &ndash; Edit Transaction Item</PageTitle>
@ -19,7 +19,7 @@
<div class="row"> <div class="row">
<button class="button button--primary" type="submit" form="editTransactionItem">Update</button> <button class="button button--primary" type="submit" form="editTransactionItem">Update</button>
<a class="button" href="/transaction/new/items">Cancel</a> <a class="button" href="/transactions/new/items">Cancel</a>
<span class="row__fill"></span> <span class="row__fill"></span>
<button class="button button--danger" type="submit" form="deleteTransactionItem">Remove</button> <button class="button button--danger" type="submit" form="deleteTransactionItem">Remove</button>
</div> </div>
@ -35,7 +35,8 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();

View File

@ -3,7 +3,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; Edit Transaction Promotion</PageTitle> <PageTitle>Groceries &ndash; Edit Transaction Promotion</PageTitle>
@ -35,7 +35,8 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();

View File

@ -3,7 +3,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction Item</PageTitle> <PageTitle>Groceries &ndash; New Transaction Item</PageTitle>
@ -16,7 +16,7 @@
<TransactionItemForm TransactionItem="TransactionItem"> <TransactionItemForm TransactionItem="TransactionItem">
<div class="row"> <div class="row">
<button class="button button--primary" type="submit">Add</button> <button class="button button--primary" type="submit">Add</button>
<a class="button" href="/transaction/new/items">Cancel</a> <a class="button" href="/transactions/new/items">Cancel</a>
</div> </div>
</TransactionItemForm> </TransactionItemForm>
@ -31,7 +31,8 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();

View File

@ -2,7 +2,8 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@layout Layout @layout Layout
@inject AppDbContext DbContext
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction</PageTitle> <PageTitle>Groceries &ndash; New Transaction</PageTitle>
@ -78,13 +79,15 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();
var itemIds = Transaction.Items.Select(item => item.ItemId); var itemIds = Transaction.Items.Select(item => item.ItemId);
itemNames = await DbContext.Items itemNames = await dbContext.Items
.Where(item => itemIds.Contains(item.Id)) .Where(item => itemIds.Contains(item.Id))
.ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name)); .ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name));
} }

View File

@ -3,7 +3,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction</PageTitle> <PageTitle>Groceries &ndash; New Transaction</PageTitle>
@ -41,7 +41,8 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
stores = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
stores = await dbContext.Stores
.OrderBy(store => store.Retailer!.Name) .OrderBy(store => store.Retailer!.Name)
.ThenBy(store => store.Name) .ThenBy(store => store.Name)
.Select(store => new StoreModel(store.Id, string.Concat(store.Retailer!.Name, " ", store.Name))) .Select(store => new StoreModel(store.Id, string.Concat(store.Retailer!.Name, " ", store.Name)))

View File

@ -3,7 +3,7 @@
@layout Layout @layout Layout
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction Promotion</PageTitle> <PageTitle>Groceries &ndash; New Transaction Promotion</PageTitle>
@ -28,7 +28,8 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();

View File

@ -2,7 +2,8 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@layout Layout @layout Layout
@inject AppDbContext DbContext
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction</PageTitle> <PageTitle>Groceries &ndash; New Transaction</PageTitle>
@ -64,7 +65,8 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
store = await DbContext.Stores using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId) .Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name)) .Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync(); .SingleAsync();

View File

@ -1,7 +1,7 @@
@using Groceries.Data @using Groceries.Data
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<form method="post" @attributes="AdditionalAttributes"> <form method="post" @attributes="AdditionalAttributes">
@* Ensure form action/method are used for implicit submission instead of barcode button *@ @* Ensure form action/method are used for implicit submission instead of barcode button *@
@ -93,11 +93,12 @@
{ {
barcode = TransactionItem.Item?.Barcodes.FirstOrDefault(); barcode = TransactionItem.Item?.Barcodes.FirstOrDefault();
items = await DbContext.Items using var dbContext = DbContextFactory.CreateDbContext();
items = await dbContext.Items
.OrderBy(item => item.Brand) .OrderBy(item => item.Brand)
.ThenBy(item => item.Name) .ThenBy(item => item.Name)
.GroupJoin( .GroupJoin(
DbContext.ItemPurchases.Where(purchase => purchase.IsLastPurchase), dbContext.ItemPurchases.Where(purchase => purchase.IsLastPurchase),
item => item.Id, item => item.Id,
lastPurchase => lastPurchase.ItemId, lastPurchase => lastPurchase.ItemId,
(item, purchases) => new { item, purchases }) (item, purchases) => new { item, purchases })

View File

@ -1,7 +1,7 @@
@using Groceries.Data @using Groceries.Data
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@inject AppDbContext DbContext @inject IDbContextFactory<AppDbContext> DbContextFactory
<form method="post" @attributes="AdditionalAttributes"> <form method="post" @attributes="AdditionalAttributes">
<div class="form-field"> <div class="form-field">
@ -56,8 +56,9 @@
{ {
selectedItemIds = Promotion?.Items.Select(item => item.Id).ToArray() ?? []; selectedItemIds = Promotion?.Items.Select(item => item.Id).ToArray() ?? [];
using var dbContext = DbContextFactory.CreateDbContext();
var itemIds = Transaction.Items.Select(item => item.ItemId); var itemIds = Transaction.Items.Select(item => item.ItemId);
itemNames = await DbContext.Items itemNames = await dbContext.Items
.Where(item => itemIds.Contains(item.Id)) .Where(item => itemIds.Contains(item.Id))
.ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name)); .ToDictionaryAsync(item => item.Id, item => string.Concat(item.Brand, " ", item.Name));
} }

View File

@ -9,11 +9,11 @@ using System.Text.Json;
[Route("/transactions")] [Route("/transactions")]
public class TransactionsController : Controller public class TransactionsController : Controller
{ {
private readonly AppDbContext dbContext; private readonly IDbContextFactory<AppDbContext> dbContextFactory;
public TransactionsController(AppDbContext dbContext) public TransactionsController(IDbContextFactory<AppDbContext> dbContextFactory)
{ {
this.dbContext = dbContext; this.dbContextFactory = dbContextFactory;
} }
[HttpGet] [HttpGet]
@ -70,6 +70,8 @@ public class TransactionsController : Controller
Item? item = null; Item? item = null;
if (barcodeData != null && barcodeFormat != null) if (barcodeData != null && barcodeFormat != null)
{ {
using var dbContext = dbContextFactory.CreateDbContext();
item = await dbContext.Items item = await dbContext.Items
.Where(item => item.Barcodes.Any(barcode => barcode.BarcodeData == barcodeData)) .Where(item => item.Barcodes.Any(barcode => barcode.BarcodeData == barcodeData))
.OrderByDescending(item => item.UpdatedAt) .OrderByDescending(item => item.UpdatedAt)
@ -105,6 +107,8 @@ public class TransactionsController : Controller
return Results.LocalRedirect("/transactions/new"); return Results.LocalRedirect("/transactions/new");
} }
using var dbContext = dbContextFactory.CreateDbContext();
var itemId = await dbContext.Items var itemId = await dbContext.Items
.Where(item => EF.Functions.ILike(item.Brand, brand) && EF.Functions.ILike(item.Name, name)) .Where(item => EF.Functions.ILike(item.Brand, brand) && EF.Functions.ILike(item.Name, name))
.Select(item => item.Id) .Select(item => item.Id)
@ -171,6 +175,8 @@ public class TransactionsController : Controller
return Results.LocalRedirect("/transactions/new/items"); return Results.LocalRedirect("/transactions/new/items");
} }
using var dbContext = dbContextFactory.CreateDbContext();
var itemId = await dbContext.Items var itemId = await dbContext.Items
.Where(item => EF.Functions.ILike(item.Brand, brand) && EF.Functions.ILike(item.Name, name)) .Where(item => EF.Functions.ILike(item.Brand, brand) && EF.Functions.ILike(item.Name, name))
.Select(item => item.Id) .Select(item => item.Id)
@ -235,6 +241,8 @@ public class TransactionsController : Controller
return Results.LocalRedirect("/transactions/new"); return Results.LocalRedirect("/transactions/new");
} }
using var dbContext = dbContextFactory.CreateDbContext();
// Work around EF trying to insert items by explicitly tracking them as unchanged // Work around EF trying to insert items by explicitly tracking them as unchanged
dbContext.Items.AttachRange( dbContext.Items.AttachRange(
transaction.Items transaction.Items

View File

@ -1,7 +1,10 @@
@using Groceries.Data @using Groceries.Data
@using Microsoft.EntityFrameworkCore
@layout Layout @layout Layout
@inject AppDbContext DbContext
@implements IDisposable
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; Transactions</PageTitle> <PageTitle>Groceries &ndash; Transactions</PageTitle>
@ -35,14 +38,16 @@
public int TotalItems { get; init; } public int TotalItems { get; init; }
} }
private AppDbContext? dbContext;
private IQueryable<TransactionModel> transactions = null!; private IQueryable<TransactionModel> transactions = null!;
private PaginationState pagination = new(); private PaginationState pagination = new();
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
transactions = DbContext.Transactions dbContext ??= DbContextFactory.CreateDbContext();
transactions = dbContext.Transactions
.Join( .Join(
DbContext.TransactionTotals, dbContext.TransactionTotals,
transaction => transaction.Id, transaction => transaction.Id,
transactionTotal => transactionTotal.TransactionId, transactionTotal => transactionTotal.TransactionId,
(transaction, transactionTotal) => new TransactionModel (transaction, transactionTotal) => new TransactionModel
@ -55,4 +60,9 @@
}) })
.OrderByDescending(transaction => transaction.CreatedAt); .OrderByDescending(transaction => transaction.CreatedAt);
} }
public void Dispose()
{
dbContext?.Dispose();
}
} }