diff --git a/MoneyMap/Services/AccountService.cs b/MoneyMap/Services/AccountService.cs index 98147b8..c045f6c 100644 --- a/MoneyMap/Services/AccountService.cs +++ b/MoneyMap/Services/AccountService.cs @@ -85,23 +85,17 @@ public class AccountService : IAccountService if (account == null) return null; - // Get cards linked to this account - var cards = await _db.Cards + // Single query with projection to avoid N+1 + var cardStats = await _db.Cards .Where(c => c.AccountId == id) .OrderBy(c => c.Owner) .ThenBy(c => c.Last4) - .ToListAsync(); - - var cardStats = new List(); - foreach (var card in cards) - { - var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id); - cardStats.Add(new CardWithStats + .Select(c => new CardWithStats { - Card = card, - TransactionCount = transactionCount - }); - } + Card = c, + TransactionCount = c.Transactions.Count + }) + .ToListAsync(); // Get transaction count for this account var accountTransactionCount = await _db.Transactions.CountAsync(t => t.AccountId == id); diff --git a/MoneyMap/Services/CardService.cs b/MoneyMap/Services/CardService.cs index 9ef6195..76eea26 100644 --- a/MoneyMap/Services/CardService.cs +++ b/MoneyMap/Services/CardService.cs @@ -55,26 +55,17 @@ public class CardService : ICardService public async Task> GetAllCardsWithStatsAsync() { - var cards = await _db.Cards + // Single query with projection to avoid N+1 + return await _db.Cards .Include(c => c.Account) .OrderBy(c => c.Owner) .ThenBy(c => c.Last4) - .ToListAsync(); - - var cardStats = new List(); - - foreach (var card in cards) - { - var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id); - - cardStats.Add(new CardWithStats + .Select(c => new CardWithStats { - Card = card, - TransactionCount = transactionCount - }); - } - - return cardStats; + Card = c, + TransactionCount = c.Transactions.Count + }) + .ToListAsync(); } public async Task CanDeleteCardAsync(int id) diff --git a/MoneyMap/Services/TransactionStatisticsService.cs b/MoneyMap/Services/TransactionStatisticsService.cs index aebe0bf..6fb5816 100644 --- a/MoneyMap/Services/TransactionStatisticsService.cs +++ b/MoneyMap/Services/TransactionStatisticsService.cs @@ -36,15 +36,19 @@ public class TransactionStatisticsService : ITransactionStatisticsService public async Task CalculateStatsAsync(IQueryable query) { - var allFilteredTransactions = await query.ToListAsync(); + // Calculate stats at database level instead of loading all transactions into memory + var stats = await query + .GroupBy(_ => 1) // Group all into one group to aggregate + .Select(g => new TransactionStats + { + Count = g.Count(), + TotalDebits = g.Where(t => t.Amount < 0).Sum(t => t.Amount), + TotalCredits = g.Where(t => t.Amount > 0).Sum(t => t.Amount), + NetAmount = g.Sum(t => t.Amount) + }) + .FirstOrDefaultAsync(); - return new TransactionStats - { - Count = allFilteredTransactions.Count, - TotalDebits = allFilteredTransactions.Where(t => t.Amount < 0).Sum(t => t.Amount), - TotalCredits = allFilteredTransactions.Where(t => t.Amount > 0).Sum(t => t.Amount), - NetAmount = allFilteredTransactions.Sum(t => t.Amount) - }; + return stats ?? new TransactionStats(); } public async Task GetCategorizationStatsAsync() @@ -64,24 +68,17 @@ public class TransactionStatisticsService : ITransactionStatisticsService public async Task> GetCardStatsForAccountAsync(int accountId) { - var cards = await _db.Cards + // Single query with projection to avoid N+1 + return await _db.Cards .Where(c => c.AccountId == accountId) .OrderBy(c => c.Owner) .ThenBy(c => c.Last4) - .ToListAsync(); - - var cardStats = new List(); - foreach (var card in cards) - { - var transactionCount = await _db.Transactions.CountAsync(t => t.CardId == card.Id); - cardStats.Add(new CardStats + .Select(c => new CardStats { - Card = card, - TransactionCount = transactionCount - }); - } - - return cardStats; + Card = c, + TransactionCount = c.Transactions.Count + }) + .ToListAsync(); } }