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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
@layout Layout
@inject AppDbContext DbContext
@inject IDbContextFactory<AppDbContext> DbContextFactory
<PageTitle>Groceries &ndash; New Transaction Item</PageTitle>
@ -16,7 +16,7 @@
<TransactionItemForm TransactionItem="TransactionItem">
<div class="row">
<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>
</TransactionItemForm>
@ -31,7 +31,8 @@
protected override async Task OnParametersSetAsync()
{
store = await DbContext.Stores
using var dbContext = DbContextFactory.CreateDbContext();
store = await dbContext.Stores
.Where(store => store.Id == Transaction.StoreId)
.Select(store => string.Concat(store.Retailer!.Name, " ", store.Name))
.SingleAsync();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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